Introduction
The Pixhost API v2 can upload images and covers, create galleries, and check the current availability of image pages.
Quick start: check an image page
curl "https://api.pixhost.cc/images/status" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data '{"urls":["https://pixhost.cc/show/8582/563_image.jpg"]}'
import requests
response = requests.post(
"https://api.pixhost.cc/images/status",
json={"urls": ["https://pixhost.cc/show/8582/563_image.jpg"]},
timeout=30,
)
print(response.json())
<?php
$curl = curl_init('https://api.pixhost.cc/images/status');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'urls' => ['https://pixhost.cc/show/8582/563_image.jpg'],
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
]);
echo curl_exec($curl);
curl_close($curl);
Supported image formats:
- JPEG (
image/jpeg) - PNG (
image/png) - GIF (
image/gif) - WebP (
image/webp) - AVIF (
image/avif)
The maximum file size is 10 MB per uploaded image.
For questions or issues, contact pixhost.to@gmail.com.
Machine-readable API description: OpenAPI 3.0 JSON.
API Domains
The API is available on all Pixhost domains:
https://api.pixhost.tohttps://api.pixhost.cchttps://api.pixho.st
Use the API hostname matching the Pixhost domain whose links you want in the response. The examples below use api.pixhost.cc.
Conventions
Requests use UTF-8 encoding. Send Accept: application/json.
All API endpoints use HTTPS. Depending on client and server capabilities, a connection can negotiate HTTP/1.1, HTTP/2, or HTTP/3. The protocol version is transport metadata, not part of the API response contract: clients must check the numeric status code (for example 200) instead of comparing a literal status line such as HTTP/1.1 200 OK.
Image and cover uploads use multipart/form-data. Do not set its Content-Type header manually when using cURL, Python Requests, or PHP cURL; the client must add the multipart boundary.
Gallery requests use application/x-www-form-urlencoded.
Successful responses with a body use application/json; charset=UTF-8. Gallery finalization succeeds with an empty body. API v2 does not require an API key.
Management URLs are opt-in so existing response bodies remain unchanged. Send include_manage_url=1 when uploading a standalone image or creating a gallery to receive a manage_url field in that creation response. Keep that URL private: it authorizes rename and delete operations and cannot be recovered later.
Web optimization is also opt-in. Send optimize_for_web=1 with an image or cover upload to remove EXIF/XMP metadata from static images, preserve an embedded ICC color profile, apply orientation, limit the longest edge to 3840 pixels, and encode a storage candidate efficiently. JPEG and PNG uploads are converted to lossy WebP at quality 90 only when the WebP candidate is both at least 4096 bytes and 2% smaller than the sanitized original-format candidate. AVIF remains AVIF. Existing lossy WebP that does not need resizing or orientation is not re-encoded; its private metadata is removed directly. Every GIF upload remains in GIF format, including a static GIF. Animated PNG, WebP, and AVIF uploads also remain unchanged.
Changelog
- 08/27/2026 - Corrected response fields, web-optimization behavior, and protocol-neutral status examples
- 08/21/2026 - Added opt-in static image web optimization
- 08/12/2026 - Added batch image status checks
- 08/12/2026 - Added opt-in image and gallery management URLs
- 07/27/2026 - Documentation updated for current domains, HTTPS, WebP, and current client libraries
- 06/21/2017 - Thumbnail size range changed to 150-500 pixels
Images
Upload Image
Uploads one image.
HTTP Request
POST https://api.pixhost.cc/images
curl --include "https://api.pixhost.cc/images" \
-H 'Accept: application/json' \
-F 'img=@image.jpg' \
-F 'content_type=0' \
-F 'max_th_size=420'
from pathlib import Path
import requests
with Path("image.jpg").open("rb") as image:
response = requests.post(
"https://api.pixhost.cc/images",
files={"img": ("image.jpg", image, "image/jpeg")},
data={
"content_type": "0",
"max_th_size": "420",
},
headers={"Accept": "application/json"},
timeout=120,
)
response.raise_for_status()
print(response.json())
<?php
$curl = curl_init('https://api.pixhost.cc/images');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'img' => new CURLFile(__DIR__ . '/image.jpg', 'image/jpeg', 'image.jpg'),
'content_type' => '0',
'max_th_size' => '420',
],
CURLOPT_HTTPHEADER => ['Accept: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
echo $response;
Response metadata
Status: 200 OK
Content-Type: application/json; charset=UTF-8
Response body
{
"name": "image.jpg",
"show_url": "https://pixhost.cc/show/8582/563_image.jpg",
"th_url": "https://t1.pixhost.cc/thumbs/8582/563_image.jpg",
"thumb_width": 420,
"thumb_height": 280
}
Form Parameters
| Parameter | Type | Required | Values | Description |
|---|---|---|---|---|
| img | file | yes | JPEG, PNG, GIF, WebP, AVIF | Image to upload |
| content_type | integer | yes | 0, 1 |
0 for safe-for-work content, 1 for NSFW content |
| max_th_size | integer | no | 150-500 |
Maximum thumbnail width or height; missing or out-of-range values use 200 |
| optimize_for_web | integer | no | 0, 1 |
Set to 1 to optimize static images for web delivery; animations remain unchanged |
| gallery_hash | string | no | Gallery identifier; supply together with gallery_upload_hash |
|
| gallery_upload_hash | string | no | Gallery upload token; supply together with gallery_hash |
|
| include_manage_url | integer | no | 0, 1 |
Set to 1 to add a management URL for a standalone image; ignored for images uploaded into a gallery |
Response Fields
| Field | Type | Description |
|---|---|---|
| name | string | Normalized stored filename without the generated numeric prefix; its extension can change when optimization converts the image |
| show_url | string | Public image page URL |
| th_url | string | Direct thumbnail URL |
| thumb_width | integer | Actual thumbnail width in pixels |
| thumb_height | integer | Actual thumbnail height in pixels |
| manage_url | string | Optional private edit/delete URL; returned only when include_manage_url=1 |
| optimization | object | Optional result returned when optimize_for_web=1 and the optimizer produces a result; describes whether optimization was applied, the selected MIME type and byte/dimension changes |
The optimization.reason field is animated when an animated PNG, WebP, or AVIF upload was intentionally left unchanged and gif_not_optimized for every GIF upload, whether static or animated. Other successful values are metadata_only, kept_avif, reencoded_webp, converted_to_webp, and webp_not_smaller. Candidate byte fields are included when candidates were encoded, allowing API clients to log why WebP was or was not selected. Web optimization is optional: if the optimizer is unavailable or fails internally, the upload can continue with the original image and omit the optimization field.
Check Image Status
Checks whether public Pixhost image pages are currently available according to the application database. This does not fetch image files or CDN nodes, so a stale CDN copy can temporarily remain accessible after the API reports deleted.
Only exact public show URLs on pixhost.to, pixhost.cc, or pixho.st and their www aliases are accepted. Historical http links are accepted in addition to https. Direct image URLs, thumbnail URLs, URLs containing credentials, ports, query strings, or fragments, and URLs on other hosts receive the per-item invalid status.
HTTP Request
POST https://api.pixhost.cc/images/status
curl --include "https://api.pixhost.cc/images/status" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data '{"urls":["https://pixhost.cc/show/8582/563_image.jpg","https://example.com/image.jpg"]}'
import requests
response = requests.post(
"https://api.pixhost.cc/images/status",
json={
"urls": [
"https://pixhost.cc/show/8582/563_image.jpg",
"https://example.com/image.jpg",
]
},
headers={"Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
print(response.json())
<?php
$curl = curl_init('https://api.pixhost.cc/images/status');
$body = json_encode([
'urls' => [
'https://pixhost.cc/show/8582/563_image.jpg',
'https://example.com/image.jpg',
],
]);
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
echo $response;
Response body
{
"results": [
{
"url": "https://pixhost.cc/show/8582/563_image.jpg",
"status": "available"
},
{
"url": "https://example.com/image.jpg",
"status": "invalid"
}
]
}
Results preserve the input order and duplicates. A syntactically valid batch returns 200 OK; malformed or foreign URL strings are reported individually as invalid instead of failing the whole batch.
JSON Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| urls | array of strings | yes | Between 1 and 100 image show URLs; each URL may contain at most 512 bytes |
The JSON request body is limited to 64 KiB. The endpoint allows a sustained rate of 10 requests per minute per client IP with a burst allowance of 5 requests. A rate-limited request returns 429 Too Many Requests with a Retry-After header. Responses use Cache-Control: no-store.
Result Statuses
| Status | Meaning |
|---|---|
| available | The exact image URL exists, is not deleted, and is not part of an unfinished or deleted gallery |
| deleted | The exact image URL or its gallery has been marked as deleted |
| not_found | No database record matches the complete directory and filename, or its gallery record is missing |
| pending | The image belongs to a gallery that has not yet been finalized |
| invalid | The value is not an accepted Pixhost public image show URL |
Upload Cover
Uploads a cover composed of a required left image and an optional right image.
HTTP Request
POST https://api.pixhost.cc/covers
curl --include "https://api.pixhost.cc/covers" \
-H 'Accept: application/json' \
-F 'img_left=@left.jpg' \
-F 'img_right=@right.png' \
-F 'content_type=0'
from pathlib import Path
import requests
with Path("left.jpg").open("rb") as left, Path("right.png").open("rb") as right:
response = requests.post(
"https://api.pixhost.cc/covers",
files={
"img_left": ("left.jpg", left, "image/jpeg"),
"img_right": ("right.png", right, "image/png"),
},
data={"content_type": "0"},
headers={"Accept": "application/json"},
timeout=120,
)
response.raise_for_status()
print(response.json())
<?php
$curl = curl_init('https://api.pixhost.cc/covers');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'img_left' => new CURLFile(__DIR__ . '/left.jpg', 'image/jpeg', 'left.jpg'),
'img_right' => new CURLFile(__DIR__ . '/right.png', 'image/png', 'right.png'),
'content_type' => '0',
],
CURLOPT_HTTPHEADER => ['Accept: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
echo $response;
Response metadata
Status: 200 OK
Content-Type: application/json; charset=UTF-8
Response body
{
"name": "left.jpg",
"show_url": "https://pixhost.cc/show/8582/568_left.jpg",
"th_url": "https://t1.pixhost.cc/thumbs/8582/568_left.jpg",
"thumb_width": 350,
"thumb_height": 175
}
Form Parameters
| Parameter | Type | Required | Values | Description |
|---|---|---|---|---|
| img_left | file | yes | JPEG, PNG, GIF, WebP, AVIF | Left cover image |
| img_right | file | no | JPEG, PNG, GIF, WebP, AVIF | Right cover image |
| content_type | integer | yes | 0, 1 |
0 for safe-for-work content, 1 for NSFW content |
| optimize_for_web | integer | no | 0, 1 |
Set to 1 to optimize the resulting static cover for web delivery; animations used to compose a two-image cover are flattened by the existing cover workflow |
| gallery_hash | string | no | Gallery identifier; supply together with gallery_upload_hash |
|
| gallery_upload_hash | string | no | Gallery upload token; supply together with gallery_hash |
|
| include_manage_url | integer | no | 0, 1 |
Set to 1 to add a management URL for a standalone cover; ignored for covers uploaded into a gallery |
Response Fields
| Field | Type | Description |
|---|---|---|
| name | string | Normalized stored filename based on the left image, without the generated numeric prefix; its extension can change when optimization converts the cover |
| show_url | string | Public cover page URL |
| th_url | string | Direct thumbnail URL |
| thumb_width | integer | Actual thumbnail width in pixels |
| thumb_height | integer | Actual thumbnail height in pixels |
| manage_url | string | Optional private edit/delete URL; returned only when include_manage_url=1 |
| optimization | object | Optional result returned when optimize_for_web=1 and the optimizer produces a result; describes the resulting cover optimization |
Galleries
Gallery creation is a three-step process:
- Create a gallery and retain both returned hashes.
- Upload images or covers with
gallery_hashandgallery_upload_hash. - Finalize the gallery.
Create Gallery
HTTP Request
POST https://api.pixhost.cc/galleries
curl --include "https://api.pixhost.cc/galleries" \
-H 'Accept: application/json' \
--data-urlencode 'gallery_name=Test gallery'
import requests
response = requests.post(
"https://api.pixhost.cc/galleries",
data={"gallery_name": "Test gallery"},
headers={"Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
print(response.json())
<?php
$curl = curl_init('https://api.pixhost.cc/galleries');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'gallery_name' => 'Test gallery',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
echo $response;
Response metadata
Status: 200 OK
Content-Type: application/json; charset=UTF-8
Response body
{
"gallery_name": "Test gallery",
"gallery_hash": "8NtKF",
"gallery_url": "https://pixhost.cc/gallery/8NtKF",
"gallery_upload_hash": "yKtOl8NbHMYJ8yWj8Mh90sIlfXC7PXT6RHW8Xua7"
}
Form Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| gallery_name | string | no | Gallery name; missing or empty creates an untitled gallery, otherwise up to 255 supported Unicode characters |
| include_manage_url | integer | no | Set to 1 to add a private gallery management URL to the response |
Response Fields
| Field | Type | Description |
|---|---|---|
| gallery_name | string | Gallery name |
| gallery_hash | string | Public gallery identifier |
| gallery_url | string | Public gallery URL |
| gallery_upload_hash | string | Private upload token valid for approximately 24 hours |
| manage_url | string | Optional private gallery edit/delete URL; returned only when include_manage_url=1 |
Finalize Gallery
Finalize the gallery after all uploads finish. Until it is finalized, the gallery and its images are not publicly available. Empty galleries are removed during finalization.
If this endpoint is not called, the current maintenance process finalizes the gallery after approximately 24 hours.
HTTP Request
POST https://api.pixhost.cc/galleries/{gallery_hash}/finalize
curl --include "https://api.pixhost.cc/galleries/8NtKF/finalize" \
-H 'Accept: application/json' \
--data-urlencode 'gallery_upload_hash=yKtOl8NbHMYJ8yWj8Mh90sIlfXC7PXT6RHW8Xua7'
import requests
response = requests.post(
"https://api.pixhost.cc/galleries/8NtKF/finalize",
data={
"gallery_upload_hash": "yKtOl8NbHMYJ8yWj8Mh90sIlfXC7PXT6RHW8Xua7",
},
headers={"Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
<?php
$curl = curl_init('https://api.pixhost.cc/galleries/8NtKF/finalize');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'gallery_upload_hash' => 'yKtOl8NbHMYJ8yWj8Mh90sIlfXC7PXT6RHW8Xua7',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
Successful response
Status: 200 OK
Content-Type: application/json; charset=UTF-8
The successful API v2 response has an empty body.
Form Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| gallery_upload_hash | string | yes | Private upload token returned when the gallery was created |
Management
Management endpoints use the random token contained in the final path segment of manage_url. The token acts as a bearer credential. Do not log it, publish it, or send it to third-party services.
Each token contains 256 bits of cryptographically secure randomness. The server stores only its SHA-256 digest, not the token contained in the URL.
Opening manage_url in a browser only displays a confirmation page. Browser GET requests never modify or delete content.
Rename Gallery
Only gallery management tokens support rename operations. Renaming does not change the public gallery URL.
Creation and rename use identical validation. Leading and trailing whitespace is removed and repeated Unicode spaces are collapsed. Names are limited to 255 supported Unicode characters and cannot contain control/format characters, line separators, HTML angle brackets, or four-byte characters such as emoji. An empty name is stored as an empty string and displayed publicly with the translated untitled-gallery label.
HTTP Request
POST https://api.pixhost.cc/management/{token}/rename
curl --include "https://api.pixhost.cc/management/REPLACE_WITH_TOKEN/rename" \
-H 'Accept: application/json' \
--data-urlencode 'name=New gallery name'
import requests
response = requests.post(
"https://api.pixhost.cc/management/REPLACE_WITH_TOKEN/rename",
data={"name": "New gallery name"},
headers={"Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
print(response.json())
Response body
{
"success": true,
"resource_type": "gallery",
"gallery_name": "New gallery name",
"gallery_url": "https://pixhost.cc/gallery/8NtKF"
}
Form Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | yes | New gallery name; an empty value restores the untitled-gallery display name, otherwise the shared 255-character policy applies |
Invalid or oversized names return 422 Unprocessable Entity with a JSON error body. The endpoint reports success only after verifying that the database preserved the exact normalized value; truncation is detected as a server error.
The endpoint also accepts an application/json object containing the name field.
Delete Image or Gallery
An image token deletes its standalone image. A gallery token deletes the gallery and all images in it.
HTTP Request
POST https://api.pixhost.cc/management/{token}/delete
curl --include -X POST \
-H 'Accept: application/json' \
"https://api.pixhost.cc/management/REPLACE_WITH_TOKEN/delete"
import requests
response = requests.post(
"https://api.pixhost.cc/management/REPLACE_WITH_TOKEN/delete",
headers={"Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
print(response.json())
Response body
{
"success": true,
"resource_type": "image",
"deleted": true
}
After successful deletion the management token is revoked. Reusing it returns 410 Gone.
Errors
API v2 reports failures primarily through the HTTP status code. Image-status, management, and gallery-creation validation errors return a JSON body; other error responses can be empty, so clients must always check the status code before decoding JSON.
The 414-417 responses below are legacy API-specific codes. Their meanings differ from the standard HTTP status descriptions and are preserved for compatibility with existing API v2 clients.
| Status | API v2 meaning |
|---|---|
| 400 | Invalid request, malformed JSON, missing parameter, missing file, or unsupported endpoint |
| 404 | Management token does not exist |
| 410 | Managed content was already deleted or the management token was revoked |
| 405 | The image-status endpoint was called with a method other than POST |
| 413 | Uploaded file or image-status JSON body exceeds its configured size limit |
| 414 | Uploaded file has an unsupported image format |
| 415 | Gallery does not exist, or the image-status request is not application/json |
| 416 | Gallery upload token is incorrect |
| 417 | Gallery could not be finalized |
| 422 | Gallery name, management operation, web-optimization option, or image-status request structure is invalid, or an upload is rejected |
| 429 | Image-status request rate limit exceeded; retry according to the Retry-After header |
| 500 | Internal server error |
Requests rejected by upload security controls can return a plain-text message instead of JSON.