Skip to content

Error Handling ​

Most Fangate API endpoints use the application's standard JSON envelope:

json
{
  "success": true,
  "errors_message": null,
  "data": {}
}

For failures:

json
{
  "success": false,
  "errors_message": "Human readable message",
  "data": null
}

Machine-readable codes ​

Every JSON error under /api carries an errors_code next to the message, so a client does not have to match on text: a named code where the API knows the exact reason, otherwise the fallback for the HTTP status (validation_failed, forbidden, not_found, ...). Validation failures add errors_field, the first field that failed. The full list is on Error codes. The fields are additive - message and status are unchanged.

json
{
  "success": false,
  "errors_message": "Confirm the AI content declaration before uploading.",
  "errors_code": "ai_attestation_required",
  "data": null
}
errors_codeHTTPWhere
ai_origin_required422POST /api/media, POST /api/products - a new file without its AI classification (see AI content).
ai_attestation_required422Same calls and PATCH /api/media/{media}/ai-origin - an AI value without ai_attested: true.
price_below_minimum422POST /api/products, PATCH /api/products/{product}/price, POST /api/products/{product}/price-links - a price under the floor of 500 minor units (read it from GET /api/app-data → products.min_price_cents).
product_text_too_short422POST /api/products, PATCH /api/products/{product}, POST /api/products/{product}/price-links - from 27 September 2026: neither title nor public_description has 15 characters (one of the two is enough; spaces at the ends do not count). Reported on public_description. A call that only attaches a file (product_id) or changes neither text is never refused for this. See POST /api/products.

The verification-required response below carries errors_code: creator_verification_required as well; data.error_code holds the same value and stays for older clients.

Rate limits ​

The OpenAPI file carries every limit machine-readable: operations with a limit of their own have an x-ratelimit object - limit per period, what it is counted per, the ip_ceiling (and ip_ceiling_agency_programs / ip_ceiling_registered_servers where agency programs and registered servers get more), and daily_limit where one applies.

Creator endpoints are throttled. Every throttled response carries X-RateLimit-Limit and X-RateLimit-Remaining; a 429 adds Retry-After (seconds to wait) and X-RateLimit-Reset (unix timestamp of the next window). The 429 body is Laravel's plain {"message": "Too Many Attempts.", "errors_code": "rate_limited"}, not the envelope. Current limits: media list and single-file reads 60/min per key and IP (120/min per IP), product list 60/min per key and IP (120/min per IP; the single-product read shares that counter for generated keys), wallet and analytics 30/min per key and IP (60/min per IP), AI-origin corrections 60/min per account, webhook redeliveries 10/min and 300/day per account (one per minute per delivery), key management see API Keys.

Agencies. An agency's API key that works inside one of its accounts - naming the account with the X-Fangate-Account header - is counted per account: every account has its own counters with the limits above (media and product reads 60/min, wallet and analytics 30/min), the same as for that account's own key. Across all of its accounts the agency's program is held to 1000/min per IP for media reads, 1000/min per IP for product reads and 500/min per IP for wallet and analytics, instead of 120 / 120 / 60. This ceiling is counted apart from every other caller behind the same address, so neither uses up the other's. A key the agency created on a creator's account is counted the same way.

Your own servers. The ceilings per IP are meant for one caller's address. If your software runs the keys of many creators or agencies from a few fixed servers - a CRM or a vault that every client connects with their own key - all of those keys share the ceiling of that address, and creator keys share the regular one (120 / 120 / 60). Send us the egress addresses of your servers (single IPs or CIDR ranges) by mail to info@fangate.app; calls with a generated key from a registered address are counted on a ceiling of their own, 1000/min per IP for media and product reads and 500/min per IP for wallet and analytics, for creator and agency keys alike - the agency program ceiling, but for every key from that address. The limit per key and account is unchanged.

Important exception ​

Not every endpoint uses the standard wrapper.

Current notable exceptions include:

  • GET /api/veriff/create
  • POST /api/veriff/decision
  • GET /api/maps/autocomplete, GET /api/maps/place-details (raw Google payloads)
  • POST /api/report - validation failures only; a stored report answers the envelope with data.id
  • GET /api/analytics/* - success and data without errors_message
  • POST /api/yoti/session/create

These endpoints should be handled according to their endpoint-specific schemas in the API reference.

Common status codes ​

StatusMeaningTypical cause
200SuccessStandard successful request
201CreatedResource created successfully
202AcceptedAsync processing has started, but work is not finished yet
204No contentRequest succeeded without a response body
400Bad requestInvalid body or malformed request
401UnauthorizedMissing or invalid bearer token
403ForbiddenToken is valid, but user cannot access the resource
404Not foundResource id or route target does not exist
422Validation / business rule failureRule violation, workflow constraint, or additional action required
500Internal server errorUnexpected backend failure

Validation failures ​

Validation failures may appear as:

  • the standard success=false envelope with errors_message
  • Laravel-style validation payloads for endpoints that do not use the wrapper

Examples:

json
{
  "success": false,
  "errors_message": "The email has already been taken.",
  "data": null
}

or, for a non-wrapped validation response:

json
{
  "message": "The given data was invalid.",
  "errors": {
    "email": [
      "The email has already been taken."
    ]
  }
}

Async upload / processing responses ​

Some content-creation and upload flows can return 202 Accepted.

This means:

  • Fangate has accepted the request
  • background processing is still happening
  • the client must poll the documented status endpoint or refresh the resource later

This is especially important for:

  • upload sessions
  • consent flows where media is not fully ready yet

When a tagged creator tries to accept or decline a consent tag before completing Veriff, Fangate currently returns a verification-required response instead of applying the decision immediately.

Expect fields such as:

json
{
  "success": false,
  "errors_message": "Verification required",
  "data": {
    "requires_verification": true,
    "verification": {
      "url": "https://..."
    }
  }
}

Upload and storage failures ​

Common failure categories in upload flows:

  • staged upload expired
  • multipart part list invalid
  • completion called before all required parts exist
  • storage provider returned an error
  • finalization job failed after upload

Client guidance:

  1. treat presigned-upload instructions as time-limited
  2. retry upload-session polling before recreating the entire product
  3. if finalization fails, surface the backend message and restart the upload flow cleanly

Authentication and authorization failures ​

ScenarioExpected result
Missing bearer token401
Invalid / revoked bearer token401
Accessing another creator's product or wallet data403
Acting on a consent tag that does not belong to the caller403 or 404 depending on resolution path

Error-handling recommendations ​

  • Always check the HTTP status code first.
  • Then inspect success, errors_message, and data where the standard envelope is used.
  • Do not assume that 200 means the workflow is fully complete. Some flows use follow-up status checks.
  • Do not assume every validation failure has the same JSON shape.
  • Log raw backend responses during integration until your client is stable.

Fangate backend developer documentation