The Kie.ai API: Keys, Base URL, and an OpenAI-Compatible First Request

Getting a Kie.ai key, the base URL, how the task-based request flow works, where the OpenAI-compatible surface fits, and the errors worth handling up front.

Lucas Hayes

Lucas Hayes

22 September 2026

The Kie.ai API: Keys, Base URL, and an OpenAI-Compatible First Request

Kie.ai sells one key across roughly a hundred image, video, music and chat models. The API is straightforward once you know three things that the getting-started page does not make obvious: where the OpenAI-compatible base URL actually lives, that media generation is task-based rather than synchronous, and that the HTTP status code is not where the errors are.

That last one will cost you an afternoon if nobody tells you. So here it is first.

The status code lies

Send an unauthenticated request and you get HTTP 200, with the real result inside the JSON body:

POST https://api.kie.ai/api/v1/jobs/createTask
→ HTTP 200
  {"code":401,"msg":"Unauthorized – Authentication failed..."}

The same holds on the credit endpoint and on the Anthropic-compatible path. Kie’s own sample code branches on response.status === 401 and 402, which cannot fire, because the transport status is 200 in every one of those cases.

Branch on body.code, not on the HTTP status. Unmatched routes are the exception and do return a genuine 404.

The documented code enum:

Code Meaning
200 Success
401 Unauthorized
402 Insufficient quota
422 Validation error
429 Rate limited
433 Subkey usage exceeded
455 Service unavailable, maintenance
501 Generation failed
505 Feature disabled

Note that 501 means your generation failed, not that the server is broken. Handle it separately from 500.

Base URL and auth

The base is https://api.kie.ai, and authentication is an ordinary bearer token. Keys look like sk-kie-... and come from the account settings page.

Authorization: Bearer YOUR_API_KEY

File uploads live on a different host, https://kieai.redpandaai.co, with URL, stream and base64 endpoints. Uploads are free, and the files auto-delete after 24 hours, so treat that host as a staging area rather than storage.

Where the OpenAI-compatible endpoint actually is

This is the thing people search for and rarely find, because the answer is not what the question assumes. There is no single global OpenAI-compatible base URL. Compatibility is exposed per model, as a path prefix on api.kie.ai.

Wire format Path
OpenAI chat completions /gpt-5-2/v1/chat/completions
OpenAI chat completions /gemini-3-8-flash-openai/v1/chat/completions
OpenAI responses API /codex/v1/responses
OpenAI responses API /grok/v1/responses
Anthropic messages /claude/v1/messages
Native Google /gemini/v1/models/gemini-3-8-flash

So an OpenAI SDK points at https://api.kie.ai/<model-slug>/v1 rather than at the bare host. Note that one documented slug contains a literal dot, gemini-3.1-pro, so do not normalise your slugs.

For Claude Code the setup is documented explicitly, and the docs specifically tell you not to append the path:

ANTHROPIC_BASE_URL=https://api.kie.ai/claude
ANTHROPIC_API_KEY="Bearer <your-key>"

ANTHROPIC_AUTH_TOKEN also works and takes the key without the Bearer prefix.

There is no /v1/models endpoint. A request to it returns a hard 404. Any OpenAI-compatible client that lists models on startup will break, and that failure looks like a broken key rather than a missing route, which is a miserable hour of debugging. Hardcode your model list.

Media generation is task-based

Chat is request and response. Everything that makes an image, video or track is a job you submit and poll.

POST https://api.kie.ai/api/v1/jobs/createTask
{ "model": "google/nano-banana-2", "input": { ... } }

→ {"code":200,"msg":"success","data":{"taskId":"dc1928bfcbc77..."}}

Then poll:

GET https://api.kie.ai/api/v1/jobs/recordInfo?taskId=<taskId>

The response carries state, which moves through waiting, queuing, generating, and then either success or fail, alongside failCode, failMsg, costTime and completeTime.

One detail worth knowing before you write the parser: resultJson is a JSON-encoded string, not an object. You parse it a second time to reach resultUrls. The original param field is the same. It is a small thing that produces a confusing type error if you assume otherwise.

Kie states the point plainly in its own docs: a 200 response to createTask means the task was created, not that it finished.

If you would rather not poll, createTask accepts a callBackUrl. Webhooks are signed with HMAC-SHA256, delivered with X-Webhook-Timestamp and X-Webhook-Signature headers, where the signature is computed over the task ID and timestamp joined by a dot.

The order I would build it in

Getting a first request working takes about ten minutes if you go in the right order, and considerably longer if you do not.

Start with the credit endpoint rather than a generation, because it is cheap, fast and tells you immediately whether your key is live. Remember to read code from the body rather than trusting the 200. If you see 401 there, the key is wrong or the Bearer prefix is missing.

Then submit one cheap image job and poll it by hand before you write any polling loop. You want to see the state transitions with your own eyes, and you want to hit the double-encoded resultJson once interactively rather than inside a parser you are also debugging.

Only after both of those work should you wire in webhooks. Signature verification is easy to get subtly wrong, and debugging it against a generation pipeline you have not yet proven is two problems at once.

The failure mode worth rehearsing deliberately is a fail state. Submit something your model will reject, watch failCode and failMsg populate, and make sure your code treats that as a normal outcome. Generation failures are routine at volume, and a pipeline that only handles success will stall on its first refusal.

Rate limits, and what is missing from them

The documented limit is 20 new generation requests per 10 seconds, applied per account, which Kie also describes as 120 per minute. Rejected requests do not enter the queue. Roughly 100 concurrent running tasks is described as typical, with no hard concurrency cap. Individual keys can additionally carry hourly, daily and total usage caps plus IP allowlisting, which is genuinely useful if you are handing a key to something automated.

What is absent is the part that makes a rate limit workable. I found no X-RateLimit-Limit, no X-RateLimit-Remaining, no X-RateLimit-Reset and no Retry-After documented anywhere, and none in observed responses. Combined with a 429 that arrives inside a 200, backing off intelligently means tracking your own request count against their published window rather than reading anything off the response.

Build that counter before you need it.

No official SDK

There is a Kie-AI organisation on GitHub with zero public repositories, nothing on PyPI, and no SDK referenced anywhere in the documentation. The packages you will find on npm are third-party wrappers maintained by individuals.

That is not disqualifying, because the REST surface is small enough to wrap in an afternoon. It does mean the integration is yours to maintain, and that a client library you adopt is one person’s side project.

Media is also not stored indefinitely. Generated files are retained for 14 days, logs and metadata for two months, and the signed download URLs expire after 20 minutes. Whatever you generate, move it to your own storage in the same job that created it.

Running it from a coding agent

One thing that makes this API more interesting than most media APIs is that Kie documents the Claude Code path directly. Point ANTHROPIC_BASE_URL at the Claude-compatible prefix and your agent’s model calls route through Kie instead of Anthropic, on the same key that reaches the image and video models.

That is convenient and it changes what you are depending on. Your agent’s reasoning and its media generation now share one vendor, one balance and one rate limit, and a problem at that vendor stops both at once. Whether that trade is worth the discount is a judgement about your own tolerance, not something an article can settle.

There is a second route worth knowing about. A community MCP server wraps the media models as agent tools, so your agent can generate an image mid-task rather than you calling the API yourself. Kie ships no official MCP server, and the community one carries the usual single-maintainer caveat.

If you take that path, set its enabled-tools list before you ship. An MCP server injects every tool’s schema into context on every turn, not just the turn you call it, and a catalogue this size is expensive to keep loaded when you are refactoring a router rather than making a video.

If you do run agents this way, the operational questions arrive quickly. Which agent is holding which key, what happened on the last run, and which outputs came from which task. Sharkly works on that layer, around the tools doing the execution: model usage keeps running on whatever subscriptions and API keys you configure in those tools, and the tasks, results and review stay in one place rather than spread across terminals.

Explore more

How to Run Muse Wonder Agents in Sharkly

How to Run Muse Wonder Agents in Sharkly

Run Meta's Muse Wonder as a Sharkly lane: a Codex provider for api.meta.ai on the Computer, the model in Custom arguments, and the long Tasks routed to it.

15 October 2026

LLM-Based Human-Agent Systems (LLM-HAS): A Practical Guide

LLM-Based Human-Agent Systems (LLM-HAS): A Practical Guide

LLM-HAS is the research name for designing humans into agent systems on purpose. The five components from the survey, translated into decisions for a team running coding agents.

13 October 2026

What Is an Agent OS? Architecture, Components, and Use Cases

What Is an Agent OS? Architecture, Components, and Use Cases

Agent OS traces to a real design: the AIOS kernel with scheduling, context, memory, storage, tool and access management. What exists today, and how to read the claim.

13 October 2026