API documentation

Upload images from your website, app or bot and get a public URL back in the same response. Everything is plain HTTPS and JSON.

Your appWebsite, bot or backend
.mgamer.online APIValidates and stores the image
Image URLReturned immediately in JSON
WebhookOptional server notification

The API works from any language that can make an HTTP request: PHP websites, JavaScript apps, Python scripts, Telegram bots, mobile backends and more.

Base URLhttps://imghost.mgamer.online/api/v1
FormatJSON responses, standard HTTP status codes
UploadsJPG, PNG, WEBP, GIF · up to 10 MB per image

Authentication

Send your API key as a Bearer token in the Authorization header of every request.

Header
Authorization: Bearer hostaura-YOUR_API_KEY

Keys always start with hostaura-. Requests without a valid key get 401 Unauthorized. Keep keys on your server whenever you can, and never commit them to a public repository.

API keys

Create as many keys as you need in the dashboard. A separate key per project (website, mobile app, Telegram bot) keeps usage, restrictions and webhooks independent.

  • Shown once. We only store a hash of your key, so copy it when it's created.
  • Not editable. To change a key, terminate it and create a new one. A terminated key stops working immediately.
  • Optional restrictions. Limit a key to a website domain or to an IP address (or CIDR range). Restrictions are enforced by the API on every request.
A domain restriction checks the Origin (or Referer) header that browsers send, so requests that don't come from a browser are rejected. Non-browser clients can forge headers, so use an IP restriction for servers and bots, and treat domain restrictions as protection against casual misuse rather than as authentication.

Image upload

POST /api/v1/images/upload

Send the file as multipart/form-data in a field named image.

ParameterTypeDescription
imagefileRequired. JPG, PNG, WEBP, GIF, up to 10 MB. The file content is verified, not just its extension.
curl -X POST https://imghost.mgamer.online/api/v1/images/upload \
  -H "Authorization: Bearer hostaura-YOUR_API_KEY" \
  -F "image=@/path/to/photo.png"

Response 201 Created

Response
{
  "success": true,
  "image": {
    "id": "img_a8F92kQx",
    "url": "https://imghost.mgamer.online/i/a8F92kQx",
    "direct_url": "https://imghost.mgamer.online/i/a8F92kQx.webp",
    "filename": "photo.webp",
    "mime_type": "image/webp",
    "size": 284512,
    "width": 1920,
    "height": 1080,
    "created_at": "2026-09-19T10:24:31Z"
  }
}

url opens the public image page. Use direct_url when you need the raw file, for example in an <img> tag, a Markdown image or a Telegram message.

List images

GET /api/v1/images

Query parameterDescription
pagePage number. Default 1.
per_pageResults per page, 1–100. Default 20.
formatOptional: jpg, png, webp or gif.
sortnewest (default), oldest, largest, smallest.
curl "https://imghost.mgamer.online/api/v1/images?page=1&per_page=20" \
  -H "Authorization: Bearer hostaura-YOUR_API_KEY"
Response
{
  "success": true,
  "images": [ { "id": "img_a8F92kQx", "url": "…", "direct_url": "…", "filename": "photo.webp", "mime_type": "image/webp", "size": 284512, "width": 1920, "height": 1080, "created_at": "2026-09-19T10:24:31Z" } ],
  "pagination": { "page": 1, "per_page": 20, "total": 1, "total_pages": 1 }
}

Get image

GET /api/v1/images/{id}

Returns one image from your account. Use the id from the upload response (for example img_a8F92kQx). Unknown IDs return 404.

curl https://imghost.mgamer.online/api/v1/images/img_a8F92kQx \
  -H "Authorization: Bearer hostaura-YOUR_API_KEY"

Delete image

DELETE /api/v1/images/{id}

Permanently deletes the file. Its public links stop working right away and an image.deleted webhook is sent.

curl -X DELETE https://imghost.mgamer.online/api/v1/images/img_a8F92kQx \
  -H "Authorization: Bearer hostaura-YOUR_API_KEY"
Response
{ "success": true, "deleted": true, "id": "img_a8F92kQx" }

Webhooks

Every API key has its own webhook. When that key uploads or deletes an image, we send a signed POST to your webhook URL. The API response always comes first, so the webhook is an optional server-to-server notification and never delays your upload.

EventSent when
image.uploadedAn image is uploaded with the key.
image.deletedAn image is deleted with the key, or an image that was uploaded with it is deleted elsewhere.
Payload
{
  "id": "evt_Kx81mPq02JdVhT4zLw9a",
  "event": "image.uploaded",
  "success": true,
  "created_at": "2026-09-19T10:24:31Z",
  "webhook_id": "whk_Nq5mX2pR8sT1vB7c",
  "image": {
    "id": "img_a8F92kQx",
    "url": "https://imghost.mgamer.online/i/a8F92kQx",
    "direct_url": "https://imghost.mgamer.online/i/a8F92kQx.webp",
    "filename": "photo.webp",
    "mime_type": "image/webp",
    "size": 284512,
    "width": 1920,
    "height": 1080
  }
}

Headers

HeaderValue
X-Webhook-IDUnique event ID (evt_…). Identical across retries, so you can use it to deduplicate.
X-Webhook-EventThe event name, such as image.uploaded.
X-Webhook-TimestampUnix time (seconds) when this attempt was signed.
X-Webhook-Signaturesha256= followed by the HMAC of the timestamp and body. See signatures.

Delivery and retries

Respond with any 2xx status within 5 seconds to acknowledge an event. Otherwise we retry with increasing delays (about 1 minute, then 5 minutes, then 30 minutes) for up to 3 attempts in total. Every attempt, HTTP status and error is recorded in your delivery log, where you can also send a test event or retry a failed one.

Webhook URLs must be public. We don't deliver to private or local addresses, and redirects are not followed.

No webhook URL? Use the hosted endpoint

If you leave the URL empty when creating a key, we generate a hosted endpoint for it (https://imghost.mgamer.online/hooks/whk_…). Events are stored there and you can read them whenever you like:

cURL
curl "https://imghost.mgamer.online/hooks/whk_YOUR_WEBHOOK_ID?limit=20" \
  -H "Authorization: Bearer hostaura-YOUR_API_KEY"

Add after=evt_… to receive only newer events. The response is { "success": true, "events": [ … ] }, with the same payloads shown above.

Webhook signatures

Each key's webhook has a signing secret (whsec_…) on the Webhooks page. To verify a request, compute an HMAC-SHA256 of timestamp + "." + raw body with that secret and compare it to the signature header. Use the raw request body, and reject old timestamps (more than 5 minutes) to block replays.

<?php
$secret    = getenv('HOSTAURA_WEBHOOK_SECRET');          // whsec_...
$body      = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);

if (!hash_equals($expected, $signature) || abs(time() - (int)$timestamp) > 300) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($body, true);
if ($event['event'] === 'image.uploaded') {
    // $event['image']['url'] ...
}
http_response_code(200);

Rate limits

  • 60 requests per minute per API key.
  • 20 uploads per minute per API key.
  • 10,000 requests per day per account.
  • Repeated failed authentication from one IP address is throttled.

Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time). When you go over, you get 429 Too Many Requests with a Retry-After header in seconds.

Errors

Errors use the same shape everywhere, together with the matching HTTP status.

Error response
{
  "success": false,
  "error": {
    "code": "invalid_api_key",
    "message": "Your API key is invalid or has been terminated."
  }
}
StatusCodeMeaning
401missing_api_key, invalid_api_key, key_terminatedThe key is missing, wrong or terminated.
403domain_not_allowed, ip_not_allowedThe key's domain or IP restriction doesn't allow this request.
403account_suspended, storage_limit_exceededThe account is suspended or out of storage.
404image_not_foundNo image with that ID on your account.
405method_not_allowedThe endpoint doesn't accept that HTTP method.
413file_too_largeYour image is larger than the allowed limit (10 MB).
422no_file, unsupported_type, invalid_imageNo file was sent, the type isn't supported, or the file isn't a valid image.
429rate_limited, upload_rate_limited, daily_limit_exceededToo many requests. See Retry-After.
500server_errorSomething went wrong on our side. Try again.
503maintenanceThe service is briefly unavailable.

Examples

Telegram bot (Python)

Download a photo a user sent to your bot, host it, and reply with the link. Restrict the key to your bot server's IP address.

bot.py
import os, requests

BOT = os.environ["TELEGRAM_TOKEN"]
KEY = os.environ["HOSTAURA_API_KEY"]

def host_telegram_photo(file_id: str) -> str:
    info = requests.get(f"https://api.telegram.org/bot{BOT}/getFile", params={"file_id": file_id}, timeout=15).json()
    photo = requests.get(f"https://api.telegram.org/file/bot{BOT}/{info['result']['file_path']}", timeout=30).content
    res = requests.post(
        "https://imghost.mgamer.online/api/v1/images/upload",
        headers={"Authorization": f"Bearer {KEY}"},
        files={"image": ("photo.jpg", photo, "image/jpeg")},
        timeout=30,
    ).json()
    return res["image"]["direct_url"]

Website upload form (PHP)

Forward a file from your own upload form to the API. Keep the key on the server.

upload.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && is_uploaded_file($_FILES['photo']['tmp_name'] ?? '')) {
    $ch = curl_init('https://imghost.mgamer.online/api/v1/images/upload');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('HOSTAURA_API_KEY')],
        CURLOPT_POSTFIELDS     => ['image' => new CURLFile($_FILES['photo']['tmp_name'], $_FILES['photo']['type'], $_FILES['photo']['name'])],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $result = json_decode(curl_exec($ch), true);
    echo $result['success'] ? '<img src="' . htmlspecialchars($result['image']['direct_url']) . '">' : htmlspecialchars($result['error']['message']);
}

Browser upload (JavaScript)

Front-end code can call the API directly when the key has a domain restriction for your site.

app.js
document.querySelector('#file').addEventListener('change', async (e) => {
  const form = new FormData();
  form.append('image', e.target.files[0]);
  const res = await fetch('https://imghost.mgamer.online/api/v1/images/upload', {
    method: 'POST',
    headers: { Authorization: 'Bearer hostaura-YOUR_DOMAIN_RESTRICTED_KEY' },
    body: form,
  });
  const { success, image, error } = await res.json();
  document.querySelector('#preview').src = success ? image.direct_url : '';
  if (!success) alert(error.message);
});