Error Reference
Every API error response follows the same shape. Machine-readable codes, human-readable messages, and an optional action object that tells you exactly what to do next.
Response Shape
{
"error": "error_code",
"message": "Human-readable explanation",
"action": {
"type": "upgrade | verify | wait | redirect",
"url": "https://...",
"label": "Button text for humans",
"seconds": 3600
},
"usage": {
"plan": "free",
"uploads_used": 100,
"uploads_limit": 100
}
}
The action and usage fields are only present on certain errors. The action.seconds field is only present for wait type actions.
Action Types
| Type | When | What To Do |
|---|---|---|
upgrade |
Quota exceeded (verified user) | Surface action.url to user — it's a signed upgrade link. |
verify |
Unverified account limit | Surface action.url to user for email verification. |
wait |
Rate limited | Wait action.seconds before retrying. |
redirect |
Registration closed | Direct user to action.url (usually the dashboard). |
Error Codes
unauthorized
HTTP 401 — Invalid or missing API key.
No action included.
{
"error": "unauthorized",
"message": "Invalid or missing API key"
}
forbidden
HTTP 403 — Valid key but insufficient permissions.
No action included.
{
"error": "forbidden",
"message": "Insufficient permissions"
}
quota_exceeded
HTTP 403 — Upload or storage limit reached.
Includes an upgrade action for verified users, or a verify action for unverified users.
Verified user (upgrade):
{
"error": "quota_exceeded",
"message": "Monthly upload limit reached.",
"action": {
"type": "upgrade",
"url": "https://img.pro/upgrade/pro?t=abc&exp=1709337600&sig=hmac...",
"label": "Upgrade to Pro ($10/mo) for 1,000 uploads"
},
"usage": {
"plan": "free",
"uploads_used": 100,
"uploads_limit": 100
}
}
Unverified user (verify):
{
"error": "quota_exceeded",
"message": "Unverified upload limit reached. Verify your email to unlock permanent storage.",
"action": {
"type": "verify",
"url": "https://img.pro/verify?t=abc&exp=1709337600&sig=hmac...",
"label": "Verify email for permanent storage"
},
"usage": {
"plan": "free",
"uploads_used": 100,
"uploads_limit": 100
}
}
registration_closed
HTTP 403 — Team's agent policy is set to "closed".
Includes a redirect action.
{
"error": "registration_closed",
"message": "This account does not accept new API key registrations.",
"action": {
"type": "redirect",
"url": "https://img.pro/keys",
"label": "Request key from dashboard"
}
}
key_limit_reached
HTTP 403 — Unverified account, max 3 active keys.
Includes a verify action.
{
"error": "key_limit_reached",
"message": "Unverified accounts can have 3 active keys. Verify your email to create more.",
"action": {
"type": "verify",
"url": "https://img.pro/verify?t=abc&exp=1709337600&sig=hmac...",
"label": "Verify email to remove limit"
}
}
rate_limited
HTTP 429 — Too many requests. Includes a Retry-After HTTP header.
For key creation, includes a wait action. For anonymous uploads, includes a redirect action to the quick-start docs.
// Key creation rate limit
{
"error": "rate_limited",
"message": "Too many key creation requests. Try again later.",
"action": {
"type": "wait",
"seconds": 60
}
}
// Anonymous upload rate limit
{
"error": "rate_limited",
"message": "Hourly limit of 20 uploads reached. Resets in 42 minutes. Get an API key for higher limits.",
"action": {
"type": "redirect",
"url": "https://img.pro/api/quick-start",
"label": "Create API Key"
},
"retry_after": 2520
}
validation_error
HTTP 422 — Invalid input (bad namespace, missing required field, etc.).
No action included. Has an errors field instead with per-field messages.
{
"error": "validation_error",
"message": "Validation failed",
"errors": {
"namespace": ["Must be lowercase alphanumeric with hyphens"],
"file": ["File is required"]
}
}
not_found
HTTP 404 — Media or resource doesn't exist.
No action included.
{
"error": "not_found",
"message": "Media not found"
}
upload_failed
HTTP 500 — File processing failed (invalid format, too large, or internal error).
No action included.
{
"error": "upload_failed",
"message": "File too large: 150.00 MB. Maximum file size is 70 MB."
}
fetch_failed
HTTP 502 / 504 — URL import couldn't fetch the source. Returns 504 if the request timed out (30 second limit).
No action included.
{
"error": "fetch_failed",
"message": "URL returned 404"
}
Timeout example (HTTP 504):
{
"error": "fetch_failed",
"message": "URL fetch timeout"
}
import_failed
HTTP 500 — URL import processing failed after fetch succeeded.
No action included.
{
"error": "import_failed",
"message": "Import failed"
}
update_failed
HTTP 500 — Media metadata update failed.
No action included.
{
"error": "update_failed",
"message": "Update failed"
}
delete_failed
HTTP 500 — Media deletion failed.
No action included.
{
"error": "delete_failed",
"message": "Delete failed"
}
Handling Errors in Code
Here's a comprehensive example showing how to handle errors, including action-based responses:
import requests
def upload_image(api_key, filepath, namespace=None):
response = requests.post(
"https://api.img.pro/v1/upload",
headers={"Authorization": f"Bearer {api_key}"},
files={"file": open(filepath, "rb")},
data={"namespace": namespace} if namespace else {}
)
if response.ok:
return response.json()
data = response.json()
action = data.get("action")
if action:
if action["type"] == "upgrade":
print(f"Limit reached. {action['label']}")
print(f"Upgrade here: {action['url']}")
elif action["type"] == "verify":
print(f"Verify your email: {action['url']}")
elif action["type"] == "wait":
import time
time.sleep(action["seconds"])
return upload_image(api_key, filepath, namespace)
raise Exception(f"Upload failed: {data['message']}")