Spark UC API

Developer reference

Handle every success and failure path

Integrations must treat three layers separately: HTTP status, job status, and per-voucher success / err_code. This page catalogs them all.

1) Three response layers

Never conflate these. A request can be HTTP 200 with a failed redeem inside.

LayerWhereMeans
HTTP Status line + optional JSON detail Auth, validation, rate limits, resource existence. Does not mean UC was credited.
Job GET /v1/jobs/{id}status / error Queue lifecycle: pending → running → done or failed.
Outcome result.success, result.results[].success, err_code, check-code ok Whether each voucher redeemed / validated. Inspect even when job status is done.
Critical: status: "done" means the worker finished. For redeem jobs, check every row’s success (and err_code on failure). Partial batches are common: some codes succeed, others return REDEEM_CODE_ALREADY_USED.

2) HTTP status codes

FastAPI-style errors use a JSON body with detail (string or object). Prefer reading detail.error when present.

HTTPWhenClient action
200 OK. For jobs: document returned (inspect status). For screenshot: binary WebP. Parse body; branch on job / outcome layers.
400 Bad input: invalid player id, missing/invalid codes, purchase choice required, screenshot not applicable. Fix payload; show detail.message to operators.
401 Missing or invalid X-API-Key. Re-copy key from Telegram bot.
403 No active plan + HTTP API add-on; or player banned on lookup. Renew subscription, or block that Player ID.
404 Unknown job; player not found (lookup); missing docs asset. Verify id / Player ID.
409 JOB_NOT_READY — screenshot while job still pending/running. Poll until done, then retry.
429 Per-minute throttle (RATE_LIMIT / SCREENSHOT_RATE_LIMIT) or daily/trial quota_exhausted. Honor Retry-After: 60; call GET /v1/quota.
500 Unexpected server error (e.g. ORDER_SCREENSHOT_FAILED). Retry with backoff; contact support if persistent.
501 WEBP_ENCODE_FAILED. Server WebP support issue — use JSON order_details instead.
502 Upstream lookup failure (LOOKUP_FAILED). Retry later; no quota charged for failed lookup.
503 SERVICE_UNAVAILABLE (temporary capacity / account path). Retry in a few minutes.

3) Structured detail.error codes

Returned on many non-2xx responses as {"detail":{"error":"…","message":"…"}} (or top-level error on some 429 bodies).

errorHTTPEndpointsMeaning
INVALID_PLAYER_ID400lookup, manual/stock redeemPlayer id failed format validation.
MISSING_CODES400check-code, manual-redeemAll codes empty after trim.
TOO_MANY_CODES400check-code, manual-redeemMore than 10 vouchers in one job.
INVALID_CODE400check-code, manual-redeemVoucher at index not 18 alphanumeric chars.
SAME_PLAN_CHOICE_REQUIRED400subscription/purchaseRe-buying active plan needs same_plan_action: extend | stack.
PLAYER_NOT_FOUND404player/lookupNo character for that Player ID.
PLAYER_BANNED403player/lookupAccount restricted; UC cannot be sent.
SERVICE_UNAVAILABLE503player/lookupTemporary upstream/account shortage.
LOOKUP_FAILED502player/lookupGeneric upstream failure.
RATE_LIMIT429audited API routesPer-minute throttle; Retry-After: 60.
SCREENSHOT_RATE_LIMIT429order/screenshotExtra screenshot-only throttle.
JOB_NOT_FOUND404order/screenshotNo job for this API key.
JOB_NOT_READY409order/screenshotJob still pending/running.
JOB_NOT_SUCCESSFUL400order/screenshotJob not done (e.g. failed).
NO_ORDER_DETAILS400order/screenshotNo success row / check-code / failed redeem.
WEBP_ENCODE_FAILED501order/screenshotServer cannot encode WebP.
ORDER_SCREENSHOT_FAILED500order/screenshotUnexpected render error.

Auth failures often use a plain string detail (Missing X-API-Key header, Invalid or revoked API key, subscription text). Quota on sync lookup may return detail: "quota_exhausted".

4) Job lifecycle

Create → poll → branch. Applies to check_code, manual_redeem, stock_redeem.

Create response (HTTP 200)

JSON · POST job
{
  "job_id": "674a1b2c3d4e5f60718293a4",
  "status": "pending"
}

Poll fields

FieldValues / notes
statuspending · running · done · failed
typecheck_code · manual_redeem · stock_redeem
resultPresent when worker finished with a result payload (typically done). Sanitized — no Midas emails/sessions.
errorString when status is failed (job never produced a usable redeem result).

Job-level error strings (status: failed)

errorMeaningClient action
quota_exhausted / quota_exhausted:…Daily/trial requests insufficient when worker ran.GET /v1/quota; wait for UTC reset or upgrade.
invalid_payloadEmpty codes / bad picks after queue.Fix request body.
invalid_player_idPlayer id rejected by worker.Validate digits before POST.
no_codes / inventory messagesStock redeem could not reserve requested denominations.GET /v1/stock/summary; lower picks.
unknown_job_type:…Internal mismatch (rare).Contact support.

5) Redeem outcome — err_code catalog

Used on manual_redeem / stock_redeem when job is done. Inspect result.results[] (batch) or top-level success / msg / err_code (single).

err_codesuccessMeaningRecommended UX
(empty / omitted) true UC credited. Row includes order_details (receipt fields). Show success + optional screenshot via job_id.
REDEEM_CODE_ALREADY_USED false Voucher already redeemed elsewhere. Mark code dead; do not retry same code.
CODE_EXPIRED false Voucher past expiry. Mark expired; do not retry.
PLAYER_NOT_FOUND false No PUBG character for Player ID. Public msg is human-readable English. Ask buyer to re-check Player ID; run lookup first.
PLAYER_BANNED false Account restricted in-game; UC cannot be sent. Refuse order / refund policy as you choose.
PLAYER_RISK_LIMITED false Player ID blocked from receiving UC (risk controls). Advise buyer to contact PUBG support.
ROLE_INACTIVE_LONG_TIME false Wrong ID or account inactive for a very long time. Ask player to log into PUBG Mobile, then retry.
REDEEM_FAILED false Neutral public failure (internal capacity/routing mapped away). Retry later; verify Player ID; contact support if repeated.
CDKEY_STATUS_INVALID false Upstream rejected voucher state (not a clean unused code). Run check-code; do not assume unused.
MISSING_PRODUCT_INFO false Could not map voucher to a product SKU. Retry once; escalate if persistent.
other / empty on failure false Transport or transient upstream issues (may include gateway-style codes internally). Show msg; retry with backoff.
Public API responses never include Midas account emails, session tokens, or raw internal routing tokens. Those map to safe msg + err_code values above.

6) Check-code outcomes

Job usually ends done with a result object. Use ok, cdkey_status, and mapped err_code.

SignalMeaning
ok: true + cdkey_status: 1Unused / available voucher (typical “safe to sell”).
ok: true + cdkey_status: 2Already used (treat as dead inventory).
err_code: INVALID_OR_EXPIRED_CODENo usable code info from upstream (reason was no_code_info).
err_code: CODE_EXPIREDConfirmed expired; may include expired_confirmed: true.
err_code: PLAYER_NOT_FOUNDPlayer context missing / not found during query path.
err_code: ROLE_INACTIVE_LONG_TIMEInactive / stale role for linked player context.
err_code: SERVICE_UNAVAILABLETemporary service/account shortage (neutral message).
cdkey_expires_at_unix / cdkey_expires_at_utcPresent when Midas returns parseable expiry.
batch: true + results[]Multi-code job; evaluate each row independently.

7) Player lookup (synchronous)

GET /v1/player/lookup?player_id=… — no job. Quota 0.25 only on HTTP 200.

HTTPBody highlightNotes
200 ok, player_id, player_name, quota_requests_charged Show nickname at checkout.
400 INVALID_PLAYER_ID or player_id is required Client validation failed.
404 PLAYER_NOT_FOUND No character — do not redeem.
403 PLAYER_BANNED Blocked account.
429 quota_exhausted Not enough remaining requests.
502 LOOKUP_FAILED No quota charged.
503 SERVICE_UNAVAILABLE Retry shortly.

8) Order screenshot

POST /api/v1/order/screenshot with {"job_id":"…"}. Success = Content-Type: image/webp bytes.

HTTPerrorWhen
200WebP body written.
404JOB_NOT_FOUNDWrong id / other account.
409JOB_NOT_READYStill pending/running.
400JOB_NOT_SUCCESSFULJob failed / not done.
400NO_ORDER_DETAILSNo successful redeem row (or check-code job).
429RATE_LIMIT / SCREENSHOT_RATE_LIMITThrottle; wait 60s.
501WEBP_ENCODE_FAILEDEncoder unavailable.
500ORDER_SCREENSHOT_FAILEDUnexpected render failure.

9) JSON examples

Representative shapes your client should parse.

Redeem success (batch row)

status: done · success path
{
  "status": "done",
  "type": "manual_redeem",
  "error": null,
  "result": {
    "player_id": "5123456789",
    "results": [
      {
        "success": true,
        "msg": "",
        "cdkey_name": "60 UC",
        "charac_name": "Nickname",
        "order_details": {
          "status": "Success",
          "product": "UC",
          "rewards": "UC* 60",
          "player_id": "5123456789",
          "charac_name": "Nickname"
        }
      }
    ]
  }
}

Redeem failure row (already used)

status: done · business failure
{
  "status": "done",
  "type": "manual_redeem",
  "error": null,
  "result": {
    "player_id": "5123456789",
    "results": [
      {
        "success": false,
        "err_code": "REDEEM_CODE_ALREADY_USED",
        "msg": "…"
      }
    ]
  }
}

Job failed (quota)

status: failed
{
  "status": "failed",
  "type": "manual_redeem",
  "result": null,
  "error": "quota_exhausted:daily"
}

HTTP validation error

HTTP 400
{
  "detail": {
    "error": "INVALID_CODE",
    "message": "Invalid voucher format at index 0.",
    "code_sample": "short"
  }
}

Rate limit

HTTP 429 · Retry-After: 60
{
  "detail": "rate_limit_exceeded",
  "error": "RATE_LIMIT"
}

10) Client checklist

Minimum branching for a production panel.

After every authenticated call

Handle 401 / 403 / 429 before parsing business data. Surface subscription problems separately from bad Player IDs.

After creating a job

Poll GET /v1/jobs/{id} until done or failed (2–3s interval). Cap wait time and show “still processing” UI.

When status is done (redeem)

Iterate results[]. Credit buyer only for success: true. Map err_code to clear storefront messages (used / expired / banned / not found).

When status is failed

Read error. If it starts with quota_exhausted, pause the queue and refresh quota. Inventory errors → restock.

Before redeem

Optional: lookup nickname; optional: check-code for inventory. Reduces refunds and support tickets.