API reference

Request and response schemas plus curl examples for the four chat protocol endpoints, GET /v1/models and the balance/billing endpoints, with an index of the media, digital-human and knowledge-base endpoints

Every public endpoint on the platform accepts the same sk-gpushare-* API key. This page details the four chat protocol endpoints plus GET /v1/models and both sets of balance endpoints; the media (image, video, music, speech), digital-human and knowledge-base endpoints have pages of their own.

EndpointProtocol / purposeDetails
POST /v1/chat/completionsOpenAI Chat — the most universal, works across vendorsthis page
POST /v1/messagesAnthropic Messages — for the Anthropic SDK directlythis page
POST /v1beta/models/{model}:generateContentGemini Native — for Google's genai SDK directlythis page
POST /v1/responsesOpenAI Responses — GPT-5.x's native protocol, with built-in toolsthis page
GET /v1/modelsOpenAI model discovery (SDKs and third-party clients call it automatically)this page
GET /v1/key/balanceThis key's remaining, used and total creditthis page
GET /v1/dashboard/billing/*OpenAI's own billing endpoints — what third-party clients call to show a balancethis page
POST /v1/images/generationsText-to-image and image-to-image (synchronous), billed per imageImage / video / music APIs
POST /v1/videos/generations (plus GET .../{id} polling and a GET list)Text-to-video and image-to-video (async task), billed per secondImage / video / music APIs
POST /v1/music/generations (plus GET .../{id} polling)AI music generation (async task), billed per generationImage / video / music APIs
POST /v1/audio/speech / /v1/audio/voicesSpeech synthesis (per character) and voice cloning (per call)Image / video / music APIs
POST /v1/videos/avatars + GET /v1/videos/clip-templatesThe avatar library and smart-edit templatesDigital human / smart edit API
POST /v1/embeddings⚠️ The embedding SKUs were withdrawn in July 2026, so no model is currently available (calls return 404 model_not_found)
POST /v1/transcripts/extractShort-video link → spoken script (synchronous), billed per callImage / video / music APIs
/api/v1/ext/wiki/* (8 read-only REST endpoints) + /mcp (an MCP server)Knowledge-base searchKnowledge base API & MCP

Base URL: https://jiuye.zsopc.com

Authentication#

See Authentication for the full story. Pick any of the four forms; they fall back in this order:

  1. the x-api-key: sk-gpushare-xxx header (recommended)
  2. the x-goog-api-key: sk-gpushare-xxx header (Google's genai SDK default — Gemini SDK users need change nothing)
  3. ?key=sk-gpushare-xxx query
  4. the Authorization: Bearer sk-gpushare-xxx header (the OpenAI and Anthropic SDK default)

One exception: GET /v1/models accepts only the Authorization: Bearer and x-api-key headers and does not support the ?key= query.


POST /v1/chat/completions#

The OpenAI Chat Completions-compatible endpoint. The most universal one, covering every chat model (70+); for the full list see Models or GET /v1/models.

Image- and video-only SKUs (Seedream, Seedance and so on) do not live here — calling them returns 503 no_channel_available. Use their own endpoints instead; see Image / video APIs.

The request#

{
  "model": "claude-sonnet-4-6",
  "messages": [
    {"role": "system", "content": "You are helpful."},
    {"role": "user", "content": "Hello"}
  ],
  "stream": false,
  "max_tokens": 1024,
  "temperature": 0.7,
  "tools": [
    {"type": "function", "function": {...}}
  ]
}
FieldRequiredTypeNotes
modelstringthe model ID; see Models
messagesarrayconversation history, with role ∈ {system, user, assistant, tool}
streambooltrue turns on SSE streaming
max_tokensintthe cap on generated tokens
temperaturefloat0-2
toolsarrayfunction tools, or the built-in {type:"web_search"} / {type:"image_generation"} (constraints below)
tool_choicestring|objectauto / none / {type:"function","function":{...}}
stream_optionsobjectwhile streaming, {"include_usage": true} puts token counts in the trailing chunk
response_formatobject{"type":"json_object"} forces JSON output

Constraints on the built-in tools ({type:"web_search"} and {type:"image_generation"}):

  • Both run through the WebSocket V2 adapter on this endpoint, so stream: true is mandatory — non-streaming returns 400 invalid_request straight away (message: Tools `web_search` and `image_generation` require `stream: true` )
  • image_generation is supported only on GPT-5.x (the x1 source); other models return 400 tool_not_supported
  • web_search is gated on what each model and channel supports, and an unsupported combination returns 400 tool_not_supported. Per-model support is in Models

Function tools ({type:"function",...}) are exempt from all of the above and work either streaming or not.

The response (non-streaming)#

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1715845200,
  "model": "claude-sonnet-4-6",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "Hello!"},
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 5,
    "total_tokens": 15
  }
}

The response (streaming)#

With stream: true you get an SSE stream where each data: line is one chunk. See Streaming.

curl#

curl https://jiuye.zsopc.com/v1/chat/completions \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.4",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

POST /v1/messages#

The Anthropic Messages-compatible endpoint.

The request#

{
  "model": "claude-sonnet-4-6",
  "max_tokens": 1024,
  "system": "You are helpful.",
  "messages": [
    {"role": "user", "content": "Hello"}
  ],
  "stream": false,
  "tools": [...]
}
FieldRequiredTypeNotes
modelstringthe model ID
max_tokensintrequired by the Anthropic protocol (unlike OpenAI)
messagesarraythe conversation, with role ∈ {user, assistant}
systemstringthe system prompt (a top-level field, not part of messages)
streambool
toolsarrayAnthropic's tool format (name / description / input_schema)
tool_choiceobject{"type":"auto"|"any"|"tool", "name": "..."}

The response (non-streaming)#

{
  "id": "msg_...",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-6",
  "content": [
    {"type": "text", "text": "Hello!"}
  ],
  "stop_reason": "end_turn",
  "usage": {"input_tokens": 10, "output_tokens": 5}
}

curl#

curl https://jiuye.zsopc.com/v1/messages \
  -H "x-api-key: $PLATFORM_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Limitations#

  • For the mainstream gemini-2.5 and gemini-3.x SKUs, prefer the Gemini Native endpoint; some gemini-* SKUs are wired up here too. The exact model × endpoint support is in the compatibility matrix
  • When the model exists but no channel serves it on this endpoint, you get 503 no_channel_available
  • The SDK injects the anthropic-version header for you; with raw curl, send 2023-06-01

Gemini Native#

Two related endpoints:

  • POST /v1beta/models/{model}:generateContent — non-streaming
  • POST /v1beta/models/{model}:streamGenerateContent — streaming

The {model} placeholder goes straight into the URL, e.g. /v1beta/models/gemini-2.5-pro:generateContent.

The request#

{
  "contents": [
    {"role": "user", "parts": [{"text": "Hello"}]}
  ],
  "systemInstruction": {
    "parts": [{"text": "You are helpful."}]
  },
  "generationConfig": {
    "maxOutputTokens": 1024,
    "temperature": 0.7
  },
  "tools": [...]
}

The response#

{
  "candidates": [{
    "content": {
      "role": "model",
      "parts": [{"text": "Hello!"}]
    },
    "finishReason": "STOP",
    "index": 0
  }],
  "usageMetadata": {
    "promptTokenCount": 10,
    "candidatesTokenCount": 5,
    "totalTokenCount": 15
  }
}

curl#

curl "https://jiuye.zsopc.com/v1beta/models/gemini-2.5-pro:generateContent?key=$PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{"parts": [{"text": "Hello"}]}]
  }'

Limitations#

  • GPT-5.x isn't supported here yet (the model exists but has no channel, so you get 503 no_channel_available); the exact matrix is in the compatibility matrix

POST /v1/responses#

The OpenAI Responses API-compatible endpoint — GPT-5.x's native protocol. Compared with /v1/chat/completions it adds two native tools, web_search and image_generation (drawing with GPT-image-2), and its responses carry full structured reasoning, output_text and tool-call fields.

Supported models#

The GPT-5 family (natively) plus the Claude SKUs on the x1 source; the exact matrix is in the compatibility matrix:

ModelNotes
gpt-5.4
gpt-5.5recommended — supports reasoning summaries and the built-in tools
claude-opus-4-6 / claude-opus-4-7 / claude-opus-4-8X1
claude-sonnet-4-6X1
claude-haiku-4-5-20251001X1

What happens when you call something else:

  • a model id that isn't in the catalogue → 400 model_not_found
  • the model exists but no channel serves it here → 503 no_channel_available (use /v1/chat/completions or that model's native endpoint instead)
  • model_not_allowed appears only when your key has an allowed_models allowlist that omits the model

The request#

{
  "model": "gpt-5.5",
  "instructions": "You are concise.",
  "input": [
    {"role": "user", "content": "Say hello in 5 words."}
  ],
  "stream": false,
  "max_output_tokens": 1024,
  "temperature": 1.0,
  "top_p": 0.98,
  "reasoning": {"effort": "medium"},
  "tools": [
    {"type": "web_search"},
    {"type": "image_generation", "size": "1024x1024"},
    {"type": "function", "name": "get_weather", "description": "...", "parameters": {...}}
  ],
  "tool_choice": "auto",
  "parallel_tool_calls": true
}
FieldRequiredTypeNotes
modelstringsee Supported models
inputarray | stringalways use the message array form (below). The GPT-5.x upstream requires input to be an array and rejects the string form
instructionsstringthe system prompt. A top-level field, not messages[0] (unlike /v1/chat/completions). The GPT-5.x upstream requires it, so always send it
streambooltrue returns an SSE event stream
max_output_tokensintthe generation cap (the Responses API uses _output_, not max_tokens)
reasoningobject{"effort": "low"|"medium"|"high"} controls how hard the model thinks internally
temperature / top_p / frequency_penalty / presence_penaltyfloatthe standard sampling parameters
toolsarraysee Tools
tool_choicestring | objectauto / none / {type:"function","name":"..."}
parallel_tool_callsbooltrue by default

The input array form (multi-turn)#

"input": [
  {"role": "user", "content": "What is 2+2?"},
  {"role": "assistant", "content": "4"},
  {"role": "user", "content": "What was my first question?"}
]

An element's content may be shortened to a plain string; vision requires content parts:

{"role": "user", "content": [
  {"type": "input_text", "text": "Describe this image."},
  {"type": "input_image", "image_url": "https://...", "detail": "auto"}
]}

Note: input_image.image_url must be a publicly reachable URL (the upstream fetches it itself); CDN thumbnails and authenticated URLs may come back as upstream_error.

Tools#

// 1. built-in web search — the model decides whether to use it and returns output_text with the answer
{"type": "web_search"}

// 2. built-in image generation — output carries an image_generation_call item (with a base64 result)
{"type": "image_generation", "size": "1024x1024"}

// 3. your own function — output carries a function_call item; you run it and send back function_call_output
{
  "type": "function",
  "name": "get_weather",
  "description": "Get current weather",
  "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
}

The response (non-streaming)#

{
  "id": "resp_0a430185e6bd1abb016a1576c7bbb08198be6868b655d19349",
  "object": "response",
  "created_at": 1779791559,
  "status": "completed",
  "model": "gpt-5.5",
  "instructions": "...",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [{"type": "output_text", "text": "Hello, hope you are well."}]
    }
  ],
  "reasoning": {"context": "current_turn", "effort": "medium", "summary": null},
  "usage": {
    "input_tokens": 25,
    "input_tokens_details": {"cached_tokens": 0},
    "output_tokens": 51,
    "output_tokens_details": {"reasoning_tokens": 38},
    "total_tokens": 76
  }
}

The output[] array is in the order things happened, and each item's type is one of:

typeMeaning
messagethe assistant's text reply, in content[].text
reasoninga summary of the internal reasoning (may be empty)
function_callthe model chose to call your function; fields are call_id, name and arguments (a JSON string)
image_generation_callthe result of the built-in image_generation tool; result is a base64 PNG

The response (streaming)#

With stream: true you get SSE whose event names are prefixed response.. The key sequence:

event: response.created            // the overall response frame (usage=null)
event: response.in_progress
event: response.output_item.added  // output item N begins
event: response.output_text.delta  // an incremental piece of text; the chunk is in delta
event: response.output_text.done   // item N's text is complete
event: response.output_item.done   // item N is finished
event: response.completed          // everything is done and usage is populated

Every data: is a single JSON object carrying a monotonically increasing sequence_number. See Streaming.

curl#

# a basic call
curl https://jiuye.zsopc.com/v1/responses \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "instructions": "You are helpful.",
    "input": [{"role": "user", "content": "Hello"}],
    "max_output_tokens": 100
  }'

# web search
curl https://jiuye.zsopc.com/v1/responses \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "instructions": "You are helpful.",
    "input": [{"role": "user", "content": "What is today date in Shanghai?"}],
    "tools": [{"type": "web_search"}]
  }'

# streaming
curl -N https://jiuye.zsopc.com/v1/responses \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "instructions": "You are helpful.",
    "input": [{"role": "user", "content": "Count 1 to 3."}],
    "stream": true
  }'

Limitations#

  • Multi-turn conversations must maintain their own history in the input array. The upstream doesn't persist conversation items (store: false semantics) and previous_response_id is unavailable — put the full history in input every turn
  • The model range is under Supported models; anything else belongs on /v1/chat/completions or its own native endpoint (error semantics above)
  • Vision is limited by what the upstream can fetch — some CDN thumbnails, authenticated URLs and hotlink-protected URLs fail. Upload the image to your own public R2 / S3 bucket and pass that URL
  • The image_generation tool doesn't force streaming here — this endpoint is an HTTP pass-through, so even with stream:false you find image_generation_call.result (a base64 PNG) in output[]. That differs from /v1/chat/completions, where image_generation runs through a dedicated WebSocket streaming adapter that forces stream:true and embeds the image as markdown inside delta.content

GET /v1/models#

The OpenAI-compatible model discovery endpoint. It's what the OpenAI SDK's client.models.list() calls, and what most third-party clients (Open WebUI, Cline, Continue and so on) use to discover models automatically.

An authentication difference#

This endpoint accepts only two header forms and does not support the ?key= query — unlike the four-way fallback elsewhere, a Gemini-style query call gets a 401:

  • Authorization: Bearer sk-gpushare-xxx
  • x-api-key: sk-gpushare-xxx

The response#

{
  "object": "list",
  "data": [
    {"id": "claude-sonnet-4-6", "object": "model", "created": 0, "owned_by": "anthropic"},
    {"id": "gpt-5.5", "object": "model", "created": 0, "owned_by": "openai"}
  ]
}
  • It returns every entry in the pricing registry, including image-, video- and embedding-only SKUs and placeholder SKUs that aren't callable yet — filter as needed when building a model menu or scripting against it
  • If your key has an allowed_models allowlist, only allowlisted entries come back
  • There's also an unauthenticated GET /api/v1/models/public returning the whole registry with pricing and capability fields (including the callable flag and endpoint_type), handy for comparison and filtering

curl#

curl https://jiuye.zsopc.com/v1/models \
  -H "Authorization: Bearer $PLATFORM_API_KEY"

GET /v1/key/balance#

Check the balance of the account behind this key. Free, and not rate-billed.

It still answers normally at a zero balance (where every other billing endpoint would 402; GET /v1/models likewise doesn't check the balance) — which is exactly the point: monitor with it before you run dry.

curl https://jiuye.zsopc.com/v1/key/balance \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
{
  "object": "key.balance",
  "remaining_usd": "12.3456",
  "used_usd": "7.6544",
  "total_usd": "20.0000",
  "expires_at": null
}
FieldMeaning
remaining_usdHow much this key can actually still spend (floored at 0): the lesser of the account balance and whatever remains of the key's spend cap
used_usdThis key's lifetime spend. Keys with a spend cap report the cap's counter; uncapped keys report the total across this key's own call log — either way it only ever counts this one key, never the account's or an enterprise owner's pooled spend
total_usdremaining_usd + used_usd. A prepaid wallet has no "granted credit" of its own; this is the synthetic denominator the billing endpoints below publish
expires_atWhen the key expires; null means never

The balance itself is shared by every key on the account rather than being per-key.

⚠️ When a key has no spend cap, remaining_usd is the backing wallet's balance — for an enterprise-issued key, that's the owner's pool. Only a key with a spend cap reports "what this key has left" instead of the whole pool. If you don't want the holder to see the pool balance, give that key a spend cap (see key spend limits).

Amounts are JSON strings (so no float precision is lost) — parseFloat them client-side.

Authentication matches every other endpoint (any of the four forms). This endpoint does not update the key's "last used" time, so polling it regularly won't register as traffic. used_usd may lag by up to 60 seconds; remaining_usd is always live.


GET /v1/dashboard/billing#

GET /v1/dashboard/billing/subscription
GET /v1/dashboard/billing/usage

/v1/key/balance above is a name this platform invented, and no third-party client knows it. These two are OpenAI's own billing endpoints: relay platforms like New API / One API call them behind their "update balance" button, so pasting the key in is enough with no extra configuration. Many desktop clients (Cherry Studio, ChatBox, NextChat, LobeChat and others) use the same pair for their "check balance" feature, though that depends on your client's version.

GET /v1/dashboard/billing/credit_grants (OpenAI's other legacy billing endpoint) is not implemented — older clients that call it get a 404. Use the two above.

Both are mounted with and without the /v1 prefix (clients differ on how they join the base URL) and return identical payloads.

curl https://jiuye.zsopc.com/v1/dashboard/billing/subscription \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
{
  "object": "billing_subscription",
  "has_payment_method": true,
  "soft_limit_usd": 20.0,
  "hard_limit_usd": 20.0,
  "system_hard_limit_usd": 20.0,
  "access_until": 0
}
curl "https://jiuye.zsopc.com/v1/dashboard/billing/usage?start_date=2026-07-01&end_date=2026-08-01" \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
{
  "object": "list",
  "total_usage": 765.44,
  "daily_costs": []
}
  • How these map to /v1/key/balance: hard_limit_usd = total_usd, and total_usage = used_usd × 100 (total_usage is in cents, OpenAI's original unit). The balance a client derives as hard_limit_usd - total_usage / 100 is therefore always exactly remaining_usd.
  • Amounts on these two endpoints are JSON numbers, not strings — that's the official schema, and clients parse them as floats.
  • access_until is the key's expiry as Unix seconds, or 0 when it never expires.
  • start_date / end_date are accepted and ignored; total_usage is always the lifetime figure. The reason: the subscription endpoint receives no date window at all, so a windowed total_usage would make the client's subtraction report a wrong balance. New API / One API ignore them for the same reason.
  • daily_costs is always an empty array. For per-day or per-model detail, use the usage pages in the console.
  • Same properties as /v1/key/balance: they answer 200 at a zero balance, and they are not billed, not rate-limited, and don't update the key's "last used" time.

For step-by-step client and relay-platform setup, see New API and third-party clients.


Error responses#

Whenever an endpoint returns HTTP 4xx/5xx, the body uses that protocol's official error schema — never a mixture:

OpenAI shape (/v1/chat/completions, /v1/responses, /v1/embeddings)#

{"error": {"message": "...", "type": "...", "code": "..."}}

Anthropic Messages (/v1/messages)#

{"type": "error", "error": {"type": "...", "message": "..."}}

Gemini Native (/v1beta/...)#

{"error": {"code": 400, "message": "...", "status": "INVALID_ARGUMENT"}}

Errors mid-stream: once a streaming request has opened (HTTP 200 is already out), a later upstream error cannot change the status code — it shows up as a truncated stream or an SSE error frame, so your client needs a fallback for "the stream never ended cleanly". See Error codes.

The most common error is an exhausted balance: HTTP 402, which in the OpenAI shape is code: "quota_exceeded" / type: "insufficient_quota" (Anthropic: type: "billing_error"; Gemini: status: "RESOURCE_EXHAUSTED"). The full truth table is in Error codes.

Limits and timeouts#

Rate limits#

The platform sets two ceilings per account (shared by all of that account's API keys, not one set each):

DimensionMeaning
Requests per minuterequests in the last 60 seconds (a sliding window)
Max concurrencyrequests in flight (not yet finished) at any one moment

Unlimited by default; the platform configures them per account when needed, and can loosen or tighten any single account. Every /v1/* request counts, including status polling for async tasks (video, music, digital human) — if your integration polls heavily, widen the interval or ask us to raise the ceiling.

Hitting a limit returns 429 rate_limit_exceeded with these headers:

HeaderMeaning
Retry-Afterhow many seconds to wait (just do as it says)
x-ratelimit-limit-requests / -remaining-requests / -reset-requeststhe per-minute request ceiling, what's left, and seconds until reset
x-ratelimit-limit-concurrency / -remaining-concurrencythe concurrency ceiling and what's left

When limits are in place, successful responses carry the x-ratelimit-* headers too, so clients can throttle themselves adaptively. You can look up your account's ceilings at jiuye.zsopc.com/dashboard/keys (the section is hidden for accounts with no limits set).

A 429 may also be an upstream rate limit passed through, unrelated to our own. Tell them apart by the headers: with x-ratelimit-* it's ours, without it's the upstream's — either way, retry with exponential backoff.

Billing and balance#

Charges come out of a single account balance (a USD wallet) shared by every API key — no key has its own budget pool, and when the balance runs out every key returns 402 at once (creating a new key doesn't help). Sign-up includes 121.32 of trial credit; top up at dflop.top/dashboard/billing (the same account as jiuye.zsopc.com). See Authentication.

Timeouts#

EndpointUpstream timeout
The four chat protocol endpoints (/v1/chat/completions, /v1/messages, /v1/responses, /v1beta/...)180s in total (streaming is bound by the same total — it just gets you the first token sooner, it isn't unbounded)
POST /v1/images/generations240s per attempt / 280s across the ladder (set client ≥300s)
POST /v1/videos/generations60s (submission only; generation is async and doesn't hold the request)
POST /v1/embeddings30s
POST /v1/transcripts/extract~55s (synchronously blocking: it creates a task internally and polls upstream to completion)

Set your SDK timeout to ≥ 200 seconds (long reasoning or tool-calling turns can run to minutes — prefer streaming). Very long jobs such as video use async polling; see Image / video / music APIs.

Automatic failover and retries#

On an upstream 5xx or connection failure the gateway switches channels and retries, trying at most 3 channels per request — which is why one request occasionally takes twice as long. A 502 upstream_unreachable or 504 upstream_timeout is safe for your client to retry.