API Docs

API Docs

OpenAI-compatible. Set base_url to https://api.eastx.ai/v1 and your existing SDK code works.

Looking for the Merchant Management API?

This page covers the end-user inference API — calls to /v1/* from any OpenAI-compatible SDK. If you’re a white-label tenant admin and want to manage brand, pricing, finance, or custom domains programmatically, use the separate Merchant Management API: token prefix sk-mgmt-*, base path /api/v1/merchant/*. Mint a token from the Console under API keys; the browsable OpenAPI spec lives at admin.eastx.ai/api/v1/merchant/docs.

/api/v1/merchant/finance/*: summary, by-model and trend use actual user consumption as revenue and recorded upstream supplier cost as costUsd, not wholesale price. Gross profit = revenue − upstream cost, matching the main site. Settleable deducts payment fees for reporting; monthly settlement and platform-cut rules are unchanged.

Quickstart

One chat completions call — pick your favorite client.

curl https://api.eastx.ai/v1/chat/completions \
  -H "Authorization: Bearer $EASTX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "messages": [
      {"role": "user", "content": "Hello!"}
    ]
  }'

Authentication

All endpoints require an API key in the Authorization header:

Authorization: Bearer sk-eastx-<your-key>
  • • Keys are issued at /keys and shown to you ONCE — store them securely.
  • • Optional per-key spend cap, RPM limit, allowed-models list, and source-IP allowlist can be set when creating a key. If an IP allowlist is set, only those IPv4/CIDR sources may call the key (403 otherwise); leaving it empty allows any IP.
  • • Revoke compromised keys immediately from the same page; revocation is instant.
POST/api/v1/auth/slider/challenge

Portal sign-in: request slider puzzle

After an explicit user action, request a PNG puzzle before password sign-in or sending an email code. Returns challengeId, backgroundImage, pieceImage, width:320, height:160, pieceSize:44 and pieceY. Expires in 120 seconds; the correct horizontal offset is never returned. White-label portals must include tenantSlug.

Request body

{
  "email": "user@example.com",
  "tenantSlug": "acme"
}

Example

curl $PORTAL_ORIGIN/api/v1/auth/slider/challenge -H "Content-Type: application/json" -d '{"email":"user@example.com","tenantSlug":"acme"}'
POST/api/v1/auth/slider/verify

Portal sign-in: verify puzzle position

Submit the user-selected horizontal offset in original 320-pixel image coordinates. Success returns {captchaToken,expiresIn:300}, bound to email, tenant and source IP for one use. Obtain a new puzzle after an incorrect or expired attempt.

Request body

{
  "email": "user@example.com",
  "tenantSlug": "acme",
  "challengeId": "<challengeId>",
  "offset": 180
}

Example

curl $PORTAL_ORIGIN/api/v1/auth/slider/verify -H "Content-Type: application/json" -d '{"email":"user@example.com","tenantSlug":"acme","challengeId":"<challengeId>","offset":180}'
  • • 180 illustrates the numeric type only; the user must select the position from the current puzzle.
POST/api/v1/auth/login

Portal sign-in: email and password

Requires a fresh captchaToken from the slider. Missing proof returns captcha_required; forged, expired or reused proof returns invalid_captcha, without a session. Repeat the slider after each attempt, including a wrong password or ambiguous network failure.

Request body

{
  "email": "user@example.com",
  "password": "<password>",
  "captchaToken": "<captchaToken>",
  "tenantSlug": "acme"
}

Example

curl -c cookies.txt $PORTAL_ORIGIN/api/v1/auth/login -H "Content-Type: application/json" -d '{"email":"user@example.com","password":"<password>","captchaToken":"<captchaToken>","tenantSlug":"acme"}'
  • • This requirement applies to portal user sign-in, not API key requests to /v1/*.
POST/api/v1/auth/email-code/send

Portal sign-in: send email code

Both login and signup sends require a fresh one-use captchaToken. After receiving the email, call /api/v1/auth/email-code/confirm with the same email, tenantSlug, purpose and six-digit code; no second slider is needed. Existing mailbox/IP/global mail limits remain enforced.

Request body

{
  "email": "user@example.com",
  "purpose": "login",
  "captchaToken": "<captchaToken>",
  "tenantSlug": "acme"
}

Example

curl $PORTAL_ORIGIN/api/v1/auth/email-code/send -H "Content-Type: application/json" -d '{"email":"user@example.com","purpose":"login","captchaToken":"<captchaToken>","tenantSlug":"acme"}'
GET/api/v1/merchant/apply/captcha

Merchant application: image challenge

Public endpoint returning {captchaId,imageSvg,expiresInSeconds:300}. Display the self-hosted SVG as an image and ask the applicant to solve the calculation. Refresh manually; no login or API key required.

Example

curl $PORTAL_ORIGIN/api/v1/merchant/apply/captcha
POST/api/v1/merchant/apply/email-code/send

Merchant application: send email code

Call only after an explicit applicant action. Trim and lowercase the email and slug. Never send email automatically.

Request body

{
  "email": "founder@acme.com",
  "slug": "acme",
  "captchaId": "<captchaId>",
  "captchaCode": "<math answer>"
}

Example

curl $PORTAL_ORIGIN/api/v1/merchant/apply/email-code/send -H "Content-Type: application/json" -d '{"email":"founder@acme.com","slug":"acme","captchaId":"<captchaId>","captchaCode":"<math answer>"}'
  • • Success returns {cooldownSeconds:60}. Wait before resending. Refresh the one-use image challenge after every send attempt. Failed sends are not shown as success.
  • • The six-digit email code is bound to this email and slug. Clear the entered code when either changes. Server verification remains authoritative.
  • • Errors include captcha_invalid, email_domain_invalid, email_code_unavailable, rate_limit_exceeded, contact_email_reserved, slug_reserved, slug_taken and validation_failed.
POST/api/v1/merchant/apply

Merchant application: submit / reverify

Submit the received six-digit emailCode. Slugs use 2–32 lowercase letters, digits or hyphens, without leading or trailing hyphens.

Request body

{
  "slug": "acme",
  "contactEmail": "founder@acme.com",
  "emailCode": "123456",
  "name": "Acme AI",
  "contactName": "Jane",
  "applyMessage": "Our use case"
}

Example

curl $PORTAL_ORIGIN/api/v1/merchant/apply -H "Content-Type: application/json" -d '{"slug":"acme","contactEmail":"founder@acme.com","emailCode":"<six digits from email>"}'
  • • Errors include email_code_invalid, email_code_unavailable, rate_limit_exceeded, contact_email_reserved, slug_reserved, slug_taken, application_changed and validation_failed. If the application changes, confirm its original identity and request a new code.
  • • Reverify an existing active or pending workspace using an exact match of its original slug and contact email with a valid code. Success returns {reverificationOnly:true}; this records verification evidence only. Existing status, business name, contact and application details remain unchanged. This does not approve or reactivate the workspace. Slug availability is advisory and must not block reverification.
GET/v1/models

List models

Returns the catalog of models your account can call. Filters out coming-soon and disabled models.

Example

curl https://api.eastx.ai/v1/models \
  -H "Authorization: Bearer $EASTX_API_KEY"
  • • Each entry has OpenAI-shape fields (id, object, created, owned_by) plus extras: display_name, input_price_per_1m, output_price_per_1m. Most OpenAI SDKs ignore unknown fields.
GET/v1/models/{id}

Retrieve a model

Lookup a single model by id. Used by some SDKs during client initialization to validate the model name.

Example

curl https://api.eastx.ai/v1/models/claude-sonnet-4-6 \
  -H "Authorization: Bearer $EASTX_API_KEY"
  • • 404 model_not_found if the id is unknown or disabled.
POST/v1/chat/completions

Chat completions

OpenAI-compatible chat-completions endpoint. Supports streaming (SSE) and non-streaming. Token usage is tracked at the upstream and your balance is debited at the end of each request.

Request body

{
  "model": "claude-sonnet-4-6",
  "messages": [
    {"role": "system", "content": "You are concise."},
    {"role": "user", "content": "Explain JWT in one sentence."}
  ],
  "max_tokens": 256,
  "temperature": 0.2,
  "stream": false
}

Example

curl https://api.eastx.ai/v1/chat/completions \
  -H "Authorization: Bearer $EASTX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "messages": [{"role":"user","content":"hi"}],
    "max_tokens": 32
  }'
  • • For streaming, set "stream": true. Each SSE event is "data: {...}" carrying an OpenAI-shape chunk with "choices[0].delta.content" for partial text and "choices[0].finish_reason" on the final non-DONE chunk. Consume until you see "data: [DONE]".
  • • Vision and tool / function calling are passed through to upstream models that support them (e.g. Claude, qwen-vl-*, GPT-class). See the examples below.
  • • Balance is pre-checked against worst-case cost (input + max_tokens × output_price). Insufficient balance returns 402.
  • • Reasoning models (e.g. gpt-5.5) bill the chain-of-thought as output_tokens — your usage.completion_tokens may exceed the visible message length. The chain itself is not exposed in the response, mirroring OpenAI’s o-series behavior.

Tool / function calling

Request body

{
  "model": "claude-sonnet-4-6",
  "messages": [
    {"role": "user", "content": "What's the weather in Paris?"}
  ],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  }]
}
  • • The model decides whether to call the tool. When it does, the response’s "choices[0].message.tool_calls" lists the function name + JSON arguments, and "finish_reason" is "tool_calls". Send back a follow-up request with the result as a {"role":"tool", ...} message.
  • • Not every upstream supports tools. Currently Anthropic Claude, Qwen-class, and most OpenAI-compatible models do; small open-source models often do not.

Vision (image inputs)

Request body

{
  "model": "claude-sonnet-4-6",
  "messages": [{
    "role": "user",
    "content": [
      {"type": "text", "text": "What is in this image?"},
      {"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}}
    ]
  }]
}
  • • Pass each user message’s "content" as an array of parts instead of a plain string. "image_url" accepts either an HTTPS URL or a "data:image/<mime>;base64,<...>" data URL.
  • • Use a vision-capable model (e.g. claude-sonnet-4-6, qwen-vl-plus). Sending an image to a text-only model is upstream-dependent — it may 400 or silently drop the image.
POST/v1/responses

Responses API (compat)

OpenAI Responses text and image input compatibility, including streaming text output. Requests use the same key authorization, model allowlist, quota and billing as Chat Completions.

Request body

{
  "model": "kimi-k3",
  "input": [{"role":"user","content":[{"type":"input_text","text":"Say hello"}]}],
  "max_output_tokens": 128,
  "stream": false
}

Example

curl https://api.eastx.ai/v1/responses \
  -H "Authorization: Bearer $EASTX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"kimi-k3","input":"Say hello","max_output_tokens":128}'
  • • Use input and max_output_tokens instead of messages and max_tokens. Returns a response object with output and usage; streams emit named response.* SSE events.
  • • Tools, previous_response_id, stored state and unsupported request fields return 400 rather than being silently ignored.
  • • input_video is an experimental provider-specific extension mapped to video_url. Video and image support depend on the selected upstream; this endpoint cannot make an upstream accept an unsupported media format.
POST/v1/messages

Anthropic Messages (compat)

Anthropic Messages API compatibility endpoint. Tools built on the Anthropic SDK — Claude Code, ZCode, and friends — work against EastX by pointing their base URL here. Requests are translated to chat-completions internally, so auth, model allowlists, rate limits, and billing behave identically.

Request body

{
  "model": "claude-sonnet-4-6",
  "max_tokens": 1024,
  "system": "You are concise.",
  "messages": [
    {"role": "user", "content": "Explain JWT in one sentence."}
  ],
  "stream": false
}

Example

curl https://api.eastx.ai/v1/messages \
  -H "x-api-key: $EASTX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 256,
    "messages": [{"role":"user","content":"hi"}]
  }'
  • • Auth accepts both the Anthropic-style "x-api-key: <key>" header and "Authorization: Bearer <key>" — same sk-eastx-* keys as everywhere else.
  • • Streaming emits standard Anthropic SSE events (message_start, content_block_delta, message_delta, message_stop). Tool use and image inputs are translated both ways.
  • • The "thinking" request parameter is accepted but not forwarded — whether a model reasons is decided upstream. Models that emit reasoning (e.g. DeepSeek) return it as a standard Anthropic "thinking" content block.
  • • POST /v1/messages/count_tokens returns a local estimate (~4 chars/token). It never hits the upstream and is free.

Using EastX from ZCode

Request body

// %USERPROFILE%\.zcode\cli\config.json  (macOS/Linux: ~/.zcode/cli/config.json)
{
  "provider": {
    "eastx": {
      "kind": "openai-compatible",
      "name": "EastX",
      "options": {
        "baseURL": "https://api.eastx.ai/v1",
        "apiKey": "sk-eastx-...",
        "apiKeyRequired": true,
        "includeUsage": true
      },
      "models": {
        "claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" },
        "deepseek-v4-pro":   { "name": "DeepSeek V4 Pro" }
      }
    }
  },
  "model": { "main": "eastx/claude-sonnet-4-6", "lite": "eastx/deepseek-v4-pro" }
}
  • • ZCode desktop: Model Settings → Add provider → API format "Chat Completions", Base URL "https://<api-host>/v1" (the /v1 suffix is required), API key sk-eastx-..., then add model ids copied verbatim from the /models page or GET /v1/models.
  • • ZCode CLI (headless): create the config.json shown above. Model references use "provider/model-id" format.
  • • Models configured with thinking levels make ZCode call /v1/messages instead of /chat/completions — supported either way as of this endpoint.
POST/v1/embeddings

Embeddings

Generate vector embeddings for one or more strings. Only models with modality=embedding are accepted here (chat models return 400 wrong_endpoint).

Request body

{
  "model": "text-embedding-v3",
  "input": "Hello, world."
}

Example

curl https://api.eastx.ai/v1/embeddings \
  -H "Authorization: Bearer $EASTX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "text-embedding-v3",
    "input": ["hello", "world"]
  }'
  • • input accepts a single string or an array of strings.
  • • Billing is by input tokens only (output_tokens = 0).
POST/v1/images/generations

Image generation

Generate images from a text prompt. We wrap Aliyun’s async task API so the client sees a synchronous response — typical latency is 3-10s. Charged per image regardless of resolution.

Request body

{
  "model": "qwen-image-plus",
  "prompt": "a red apple on a wooden table",
  "n": 1,
  "size": "1024x1024"
}

Example

curl https://api.eastx.ai/v1/images/generations \
  -H "Authorization: Bearer $EASTX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-image-plus",
    "prompt": "a red apple on a wooden table",
    "n": 1,
    "size": "1024x1024"
  }'
  • • Response shape: {"created":<ts>,"data":[{"url":"<signed-oss-url>","revised_prompt":"..."}]}.
  • • n is capped at 4. Sizes supported depend on the model; 1024x1024 works everywhere.
  • • URLs are temporary signed OSS links — download and re-host within ~1 hour.
  • • Internal polling deadline is 90s; long jobs return 504 upstream_timeout.

Billing

Usage is billed per request in USD and debited from your balance when the request completes. Prices are listed per 1M tokens on the pricing page.

  • • Settled on actual usage: you are charged on the token counts the upstream reports for the request (input + output), not on estimates. Failed requests are not charged.
  • • Pre-flight balance check: before a call is forwarded we check your balance against a worst-case estimate (input + max_tokens at the most expensive tier the request could land in). Insufficient balance returns 402 before any upstream call is made.
  • • Tiered pricing: some models are priced in tiers rather than at one flat rate — the rate depends on how long the input is, whether the model reasoned, and on some models the time of day. Tiers can differ by 10x between the cheapest and priciest band, and each request is billed at the tier it actually lands in, based on its real input token count. Every model with tiers is marked on the pricing page with a “Tiered” badge — expand it to see the rate for each band. The headline price for such a model is its cheapest band.
  • • How the thinking tier is decided: a request is billed at the thinking rate if it asked for thinking (enable_thinking, reasoning_effort, or thinking.type) OR if the response actually contains reasoning content. Some models reason by default without being asked, and the upstream charges the thinking rate whenever they do.
  • • Cache hits: when the upstream reports a prompt-cache hit, the cached portion of the input is billed at that model’s cache-hit rate instead of the full input rate; the rest of the input is billed normally. On tiered models the cache-hit rate is tiered too, and follows the same band as the request.
  • • Checking a charge: the tier each call was billed at is shown next to that call on your dashboard. If a charge still looks wrong, send us the request id and we will reconcile it against the upstream bill.

Error codes

All errors return JSON of shape {"error":{"code":"...","message":"...","type":"..."}}. HTTP status maps to error.code as follows.

The type field groups codes into families so you can branch on it without pattern-matching every name: auth_error (key issues), invalid_request_error (your input), rate_limit (RPM caps), billing_error (balance / cap), upstream_error (provider side), server_error (rare — our side).

HTTPerror.codeWhen you see it
400bad_requestRequired field missing or malformed body. Inspect error.message for specifics.
400wrong_endpointModel modality does not match endpoint (e.g. chat model on /v1/embeddings).
401missing_keyNo Authorization header. Send "Authorization: Bearer sk-eastx-...".
401invalid_keyKey not found or revoked. Generate a new one at /keys.
402insufficient_balanceWorst-case cost exceeds account balance. Top up via /recharge.
402cap_reachedPer-key spend cap (balanceCapUsd) reached. Raise it or rotate the key.
403disabledAPI key was disabled by an admin.
403expiredAPI key expiresAt is in the past.
403model_not_allowedThis key has a model allowlist and the requested model is not on it. Edit the key at /keys or use an unrestricted key.
404model_not_foundUnknown model id. Call /v1/models to see what is available.
429rate_limit_exceededPer-key requests-per-minute (RPM) limit hit. Slow down or split across keys.
429tenant_rate_limit_exceededTenant-wide RPM ceiling reached — aggregated across every key in your tenant. Distinct from the per-key 429 above. Ask your tenant admin to raise the cap.
500pricing_misconfiguredThe model is missing required price config on our side (rare admin-side bug). Retry shortly; if it persists, contact support with the model id.
502upstream_unreachableCouldn't connect to the upstream provider. Usually transient — retry with backoff.
502upstream_errorUpstream returned an error. The response message has upstream details + upstream_status.
502image_generation_failedUpstream image task ended without success (returned FAILED or empty). Retry, change the prompt, or try a different image model.
503upstream_unavailableAll upstream tokens for the channel are in cooldown or disabled. We are aware.
504upstream_timeoutImage generation took longer than 90s. Submit again; same prompt may succeed.

Seedance video tasks

Create a task with your EastX API key, then query the returned id. Read content.video_url when succeeded. Video models use the task endpoint, not chat completions.

  • seedance-2-0 / seedance-2-0-fast / seedance-2-0-mini: 4–15 seconds; seedance-2-5: 4–30 seconds.
  • Query GET /v1/models/{model} for video_pricing and supported priced resolutions.
  • Ratios: 16:9, 9:16, 1:1, 21:9, 4:3, 3:4. Defaults: 4 seconds, audio off.

Create a task

curl 'https://api.eastx.ai/v1/contents/generations/tasks' \
  -H "Authorization: Bearer $EASTX_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: my-video-001' \
  -d '{"model":"seedance-2-5","content":[{"type":"text","text":"A paper plane glides over a lake at sunrise."}],"resolution":"720p","duration":4,"ratio":"16:9","generate_audio":false}'

Query a task

curl 'https://api.eastx.ai/v1/contents/generations/tasks/<task_id>' \
  -H "Authorization: Bearer $EASTX_API_KEY"

queued and processing are pending; succeeded and failed are terminal. Poll the same task about every 10 seconds. Creation reserves credit and key budget; success settles usage.total_tokens and confirmed failure releases the hold. Polls are not billed again.

Reuse the same Idempotency-Key and body when retrying a creation. For pending_confirmation, query the existing task and contact support; do not create again. billing_pending requires sufficient balance or a higher original key budget before the video can be released.

Add references to content[]: image_url with reference_image, first_frame or last_frame; video_url with reference_video; audio_url with reference_audio. Use HTTPS URLs or authorized asset:// references, not local paths or Base64.

Model service status

GET /v1/models and model details include service_status: normal when an upstream is available, paused when service is paused. Automatically paused models remain listed and accept requests using the same model ID for recovery. Manually disabled or maintained models never recover automatically.

Explicit upstream quota errors pause that upstream. A model stays normal while any permitted upstream is available. After a 10-minute cooldown, the next real request may attempt recovery, with one concurrent recovery attempt per upstream token. No scheduled probes or extra test requests are sent. If all upstreams are unavailable, the API returns 503; retry later.

Routing preferences

Choose default, automatic, price, speed or reliability in API key settings. Applies to chat models with multiple available offers; model permissions and IP restrictions still apply.

{
  "model": "gpt-6.1-sol",
  "routing_priority": "price",
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ]
}

Override priority: routing_priority → provider.sort → x-eastx-routing header → API key setting. Values: default, auto, price, speed, success_rate. Supported on /v1/chat/completions and /v1/messages.

Price ranking uses estimated request charges. Speed and reliability use the tenant’s last 24 hours, with conservative estimates for missing history; the fastest response is not guaranteed. Default routing retains platform order and appends fallback offers. The catalog shows maximum rates; billing uses the successful route and tier, respecting tenant price overrides. Balance checks cover the most expensive eligible fallback.

Rate limits & quotas

  • • Per-key RPM: each API key has a requests-per-minute limit (default 60). Exceeding it returns 429 rate_limit_exceeded. Raise the limit on a key in the /keys page.
  • • Tenant-wide RPM ceiling: on white-label tenants, the tenant admin can set a tenant-level RPM cap that applies to all keys in the tenant combined. Hitting it returns 429 with tenant_rate_limit_exceeded — a distinct code from the per-key 429 so you can tell the two apart in your retry logic. Splitting traffic across more keys doesn’t help here; ask your tenant admin to raise the cap.
  • • Account balance: every successful request is debited from your USD balance. Insufficient balance returns 402; top up via /recharge.
  • • Per-key spend cap: optional balanceCapUsd cuts off a key after a cumulative spend. Useful for embedding short-lived keys in client apps.
  • • Upstream cooldowns: if all upstream tokens for a channel are throttled or erroring, the gateway returns 503 upstream_unavailable. Retry on backoff.