VibeCode API
Use VibeCode models from your own tools or code with a single key. The API follows the OpenAI format: swap the base URL and the key, and everything else stays the same. You pay per token from your Credit balance.
1Set up your account and Credit
API keys are created from the same VibeCode account you use in the app. No account yet? Sign up for free in the Dashboard.
The API is paid from your Credit wallet balance only. Pro and Max plan allowances are not used, and the daily limits of the app do not apply here.
To create a key you need to have topped up or bought a plan through QRIS, with the payment settled. Manual top-ups by an admin do not count. Top up on the Credit page with any amount in multiples of Rp1,000, from Rp5,000 to Rp100,000. That amount does not include the 13% tax and admin fee.
2Create a key
Your keys
Create, view, and revoke keys in the Dashboard. A new key is shown once, right after it is created.
Open API KeysA key is shown only once, right after it is created. The server stores only its fingerprint (hash), so a lost key cannot be viewed again: revoke it, then create a new one. One account can have at most 10 active keys and can create at most 30 new keys per day.
Name each key after the app or tool that uses it, so you can revoke keys one by one.
3Your first request
The API base URL is https://api.vibecodeagent.my.id/v1. Send your key in the Authorization: Bearer sk-vc-… header. Keep the key in an environment variable, not in your code:
export VIBECODE_API_KEY="sk-vc-..."curl https://api.vibecodeagent.my.id/v1/chat/completions \
-H "Authorization: Bearer $VIBECODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Halo! Perkenalkan dirimu dalam satu kalimat."}]
}'import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.vibecodeagent.my.id/v1",
api_key=os.environ["VIBECODE_API_KEY"],
)
jawaban = client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[{"role": "user", "content": "Halo! Perkenalkan dirimu dalam satu kalimat."}],
)
print(jawaban.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.vibecodeagent.my.id/v1",
apiKey: process.env.VIBECODE_API_KEY,
});
const jawaban = await client.chat.completions.create({
model: "deepseek-v4.1-flash",
messages: [{ role: "user", content: "Halo! Perkenalkan dirimu dalam satu kalimat." }],
});
console.log(jawaban.choices[0].message.content);Replace deepseek-v4.1-flash with an id from the model list. The response is a standard chat completion: the text is in choices[0].message.content, and usage holds the number of tokens billed.
Request parameters
The only chat route is POST /v1/chat/completions. The parameters passed on to the model are in this table; other parameters are dropped without an error.
| Parameter | Description |
|---|---|
model | Required. An id from the model list. |
messages | Required. An array of messages in the OpenAI format. |
tools | Function calling. Works only on models that support it. |
tool_choice | Function calling. Works only on models that support it. |
parallel_tool_calls | Function calling. Works only on models that support it. |
stream | Set to true to receive Server-Sent Events. |
max_tokens | An integer, at least 1. If both are present, the larger one is used. |
max_completion_tokens | An integer, at least 1. If both are present, the larger one is used. |
temperature | Passed on to the model; the effect depends on the model. |
top_p | Passed on to the model; the effect depends on the model. |
frequency_penalty | Passed on to the model; the effect depends on the model. |
presence_penalty | Passed on to the model; the effect depends on the model. |
stop | Passed on to the model; the effect depends on the model. |
seed | Passed on to the model; the effect depends on the model. |
n | Only 1. |
response_format | Passed on to the model; see the Response format section. |
user | Passed on to the model. |
stream_options | Only include_usage is read: when true, the last chunk carries usage. |
models is rejected (400 unsupported_parameter), and any n other than 1 is rejected (400 unsupported_value).Streaming
Send "stream": true to receive Server-Sent Events. Each chunk is a chat.completion.chunk, and the stream ends with data: [DONE].
The usage chunk is sent only if you ask for it with stream_options: {"include_usage": true}. Your bill does not depend on it: we count from the usage the model reports, and if those figures do not arrive, see How billing works.
curl -N https://api.vibecodeagent.my.id/v1/chat/completions \
-H "Authorization: Bearer $VIBECODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Tulis haiku tentang senja."}],
"stream": true,
"stream_options": {"include_usage": true}
}'import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.vibecodeagent.my.id/v1",
api_key=os.environ["VIBECODE_API_KEY"],
)
stream = client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[{"role": "user", "content": "Tulis haiku tentang senja."}],
stream=True,
stream_options={"include_usage": True},
)
for bagian in stream:
if bagian.choices:
print(bagian.choices[0].delta.content or "", end="", flush=True)
if bagian.usage:
print(f"\n[token: {bagian.usage.prompt_tokens} masuk, {bagian.usage.completion_tokens} keluar]")import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.vibecodeagent.my.id/v1",
apiKey: process.env.VIBECODE_API_KEY,
});
const stream = await client.chat.completions.create({
model: "deepseek-v4.1-flash",
messages: [{ role: "user", content: "Tulis haiku tentang senja." }],
stream: true,
stream_options: { include_usage: true },
});
for await (const bagian of stream) {
const teks = bagian.choices?.[0]?.delta?.content;
if (teks) process.stdout.write(teks);
if (bagian.usage) {
console.log(`\n[token: ${bagian.usage.prompt_tokens} masuk, ${bagian.usage.completion_tokens} keluar]`);
}
}Function calling (tools)
Send function definitions in tools; the model replies with tool_calls. In streaming mode, arguments arrives in pieces with the same index, so join the pieces before parsing the JSON. Not every model supports function calling: try it first on the model you pick.
curl https://api.vibecodeagent.my.id/v1/chat/completions \
-H "Authorization: Bearer $VIBECODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Cuaca di Bandung sekarang?"}],
"tools": [{
"type": "function",
"function": {
"name": "cuaca_kota",
"description": "Ambil cuaca terkini untuk satu kota.",
"parameters": {
"type": "object",
"properties": {"kota": {"type": "string"}},
"required": ["kota"]
}
}
}]
}'import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.vibecodeagent.my.id/v1",
api_key=os.environ["VIBECODE_API_KEY"],
)
alat = [{
"type": "function",
"function": {
"name": "cuaca_kota",
"description": "Ambil cuaca terkini untuk satu kota.",
"parameters": {
"type": "object",
"properties": {"kota": {"type": "string"}},
"required": ["kota"],
},
},
}]
jawaban = client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[{"role": "user", "content": "Cuaca di Bandung sekarang?"}],
tools=alat,
)
panggilan = jawaban.choices[0].message.tool_calls
if panggilan:
print(panggilan[0].function.name, panggilan[0].function.arguments)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.vibecodeagent.my.id/v1",
apiKey: process.env.VIBECODE_API_KEY,
});
const alat = [{
type: "function",
function: {
name: "cuaca_kota",
description: "Ambil cuaca terkini untuk satu kota.",
parameters: {
type: "object",
properties: { kota: { type: "string" } },
required: ["kota"],
},
},
}];
const jawaban = await client.chat.completions.create({
model: "deepseek-v4.1-flash",
messages: [{ role: "user", content: "Cuaca di Bandung sekarang?" }],
tools: alat,
});
const panggilan = jawaban.choices[0].message.tool_calls;
if (panggilan) {
console.log(panggilan[0].function.name, panggilan[0].function.arguments);
}Send the function result back as a role: "tool" message with the same tool_call_id, as you would with OpenAI.
Response format
response_format is passed on to the model. {"type": "json_object"} works only on models that support it; other models may ignore it or reject it with a 400 upstream_rejected error. Ask for JSON in the prompt too, and check the result before you use it.
{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Beri tiga warna dasar sebagai objek JSON."}],
"response_format": {"type": "json_object"}
}Models and pricing
The list below is read live from GET /v1/models, with no sign-in. Prices are in Credit per 1 million tokens.
Loading the model list…
A figure such as $0.292 Credit is VibeCode Credit, not US dollars.
The price you are billed is the same as in the app. Prices in the list already include the minimum rate: $0.025 Credit per 1 million input tokens and $0.05 Credit per 1 million output tokens.
GET /v1/models/{id} returns a single model; an id that is not available gets a 404 model_not_found.
curl https://api.vibecodeagent.my.id/v1/modelsErrors
Errors are returned as an OpenAI-style error object, and the messages are in English:
{
"error": {
"message": "Invalid API key.",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}| code | Status | Meaning |
|---|---|---|
missing_api_key | 401 | The Authorization header is missing. |
invalid_api_key | 401 | The key is wrong or has been revoked. |
account_disabled | 403 | API access for your account was disabled by an admin. |
rate_limit_exceeded | 429 | Over the per-account requests-per-minute limit (usually 20). Follow Retry-After. |
capacity_exhausted | 429 | Model capacity is full. Try again after Retry-After. |
insufficient_balance | 402 | Your balance is below the estimated cost of the request; the message includes the figures. Top up, or lower max_tokens. |
model_not_found | 404 | The model id does not exist or is not available through the API. |
request_too_large | 413 | The request body is larger than 16 MiB. A body sent without a Content-Length header (chunked) is limited to 32 MiB. |
invalid_request_body | 400 | The request body is not valid JSON. |
unsupported_parameter | 400 | The parameter is not supported (param names it). |
unsupported_value | 400 | The parameter value is not supported, e.g. n other than 1. |
invalid_value | 400 | The parameter value is invalid, e.g. max_tokens is not an integer of at least 1. |
upstream_rejected | 400 | The model rejected your request, e.g. because of a parameter that model does not support. |
upstream_error | 502 | The model or its provider failed to answer. Try again. |
service_unavailable | 503 | The service is temporarily unavailable. Try again later. |
internal_error | 500 | An unexpected error on our side. Try again. |
unknown_url | 404 | The address is not recognized. Check the path and the /v1 prefix: only /v1/chat/completions, /v1/models, and /v1/models/{id} are available. |
method_not_allowed | 405 | That HTTP method is not supported at that address; the Allow header lists the ones that are. |
Every response carries an x-request-id header; include it when you report a problem.
Rate limits and queueing
The limit is usually 20 requests per minute per account (a 60-second sliding window), counted across all of your keys; the figure for your account is shown in the Dashboard, under API Keys. Every chat response that passes the key check carries x-ratelimit-limit-requests, x-ratelimit-remaining-requests, and x-ratelimit-reset-requests.
Model capacity is shared with the VibeCode app. When it is full, your request queues for up to about 45 seconds; if it is still full, you receive a 429 capacity_exhausted. Follow the Retry-After header (in seconds) exactly as given: its value can be as low as 1 second.
With "stream": true, the model must start sending its answer within 30 seconds. Without stream, the whole answer must finish within 30 seconds; otherwise you receive a 502 upstream_error (we may retry first, so the total wait can be longer); use "stream": true for long answers.
How billing works
- Each side (input and output) is rounded up to the smallest unit of Credit.
- Before the model runs, we reserve an estimated cost from your balance: input tokens are estimated from the size of the request (about 4 bytes per token), plus the output limit. When the request finishes, the difference is returned.
- If you do not set
max_tokensormax_completion_tokens, we set an output limit as high as your balance can pay for, up to 32,768 tokens. A long answer may then end withfinish_reason: "length"; setmax_tokensyourself for long answers. - If your balance is not enough for that estimate, the request is rejected with 402 before the model runs.
- Billing through the API never takes your balance below zero. If the actual cost exceeds the estimate and what is left cannot cover it, you are charged what the balance can pay; the excess is not billed.
- If the
usagefigures from the model do not arrive (e.g. the stream from the model is cut off), we bill according to the text already sent (about 4 bytes per token) and mark it "estimated" in the log. - One short identity sentence (about 30 tokens) is added to your request so the model knows its name; it is billed as input tokens too.
- Request logs (without message content) are kept for 30 days; daily summaries remain stored. See them on the Credit page.
- If our server restarts exactly while your request is running, the balance reserved for that request can stay held. If your balance seems short, contact the VibeCode admin and mention the
x-request-id.
Third-party tools
Any tool that can use an "OpenAI compatible" endpoint works with three fields: the base URL https://api.vibecodeagent.my.id/v1, a sk-vc-… key, and a model id from the list. Menu names may differ between versions of a tool.
Cursor
In the model settings, enter your key as the OpenAI API Key and turn on the override of the OpenAI base URL with the address above, then add a custom model id. Which features can use a custom model is decided by Cursor.
Cline and Roo Code
Choose the "OpenAI Compatible" provider, then fill in Base URL, API Key, and Model ID.
Continue
Add the model to the config file and replace sk-vc-... with your key:
models:
- name: VibeCode
provider: openai
model: deepseek-v4.1-flash
apiBase: https://api.vibecodeagent.my.id/v1
apiKey: sk-vc-...n8n
In the OpenAI Chat Model node, create a credential with your key as the API Key and change the Base URL to the address above.
Open WebUI
In Admin Settings, Connections, add an OpenAI connection with the URL and key above.
/v1/chat/completions and /v1/models. Tool features that need the Responses API, embeddings, or image and audio generation will not work.Key safety
- Do not put a key in public frontend code. This API allows requests from a browser on any origin (open CORS), so anyone who reads your page source can spend your balance.
- Keep keys in environment variables or a secrets store, one key per application.
- A leaked key: revoke it in the Dashboard, under API Keys. Revocation takes effect immediately, and the next request with that key is answered with 401.
- We never ask for your key by email or chat.
- The routes and tokens that the VibeCode app and web Chat use for your account are for those two only. Programmatic access to the models goes through this one route: a
sk-vc-…key. Details are in the Terms of Service.