Reference
First token in five minutes.
The API is OpenAI-compatible. Change the base URL, keep the client you already have, and read the rest of this sheet for the surface that goes beyond the schema.
SEC.01 — GETTING STARTED
Quickstart
Every request goes to https://api.idclinktech.com/v1. Send a bearer token, name a model from the catalog, and the gateway resolves the route for you.
FIG.01 — REQUEST LIFECYCLE
curl https://api.idclinktech.com/v1/chat/completions \
-H "Authorization: Bearer $IDCLINKTECH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5",
"messages": [{"role": "user", "content": "Say hello."}]
}'
No SDK required. Any HTTP client that speaks the OpenAI schema already speaks this one.
Request one at sales@idclinktech.com with a short note on what you are building. Trial credit is sized to validate a real workload, not just a smoke test.
Authentication
Bearer-token authentication. Keys are scoped to a project, look like sk-idc-…, and can be rotated independently. Keep the key server-side; there is no browser-safe key format.
| Request header | Required | Notes |
|---|---|---|
Authorization |
Yes | Format: Bearer sk-idc-… |
Content-Type |
Yes | application/json |
X-IDC-Project |
No | Bill the call against a specific project on a multi-project key |
X-IDC-Region |
No | Region hint: us-west, eu-fra, ap-sg |
X-IDC-Route-Tier |
No | Cap the tier the router may pick — see routing tiers |
X-IDC-No-Retention |
No | true forces a zero-retention route or fails closed |
Samples on this page read the key from IDCLINKTECH_API_KEY. A rejected key returns 401 before the request reaches any upstream, so a bad key never costs you tokens.
Model names
Pass the model as a string. The value is the API ID printed in the catalog — stable across regions, with no replica or region encoded in the name. Six of the thirty-six:
| API ID | Provider | Tier | CTX |
|---|---|---|---|
claude-fable-5 |
Anthropic | Frontier | 1M |
gpt-5.6-sol |
OpenAI | Frontier | 1M |
gemini-3.6-flash |
Frontier | 1M | |
deepseek-v4-pro |
DeepSeek | Frontier | 1M |
kimi-k3 |
Moonshot | Frontier | 1M |
mistral-medium-3-5 |
Mistral | Frontier | 256K |
- Unknown ID —
404 model_not_found, raised at the gateway. - Retired ID — keeps resolving to its documented successor for a published alias window, the way a 301 works for URLs. Every window is announced in the changelog before it opens.
- Closed weights resolve to their provider; open weights resolve to managed pools.
SEC.02 — CORE API
Chat completions
Request and response shapes mirror the OpenAI schema exactly. Provider dialects — system-prompt placement, tool envelopes, image parts — are normalized at the gateway, never in your adapter code.
| Field | Type | Notes |
|---|---|---|
model |
string | Required. Catalog API ID. |
messages |
array | Required. Roles: system, user, assistant, tool. |
max_tokens |
integer | Output cap. Defaults to the model's own ceiling. |
temperature |
number | 0 – 2, normalized across providers. |
top_p |
number | Nucleus sampling. Send this or temperature, not both. |
stream |
boolean | Server-sent events. See streaming. |
stop |
string / array | Up to four stop sequences. |
tools |
array | Function definitions. Pairs with tool_choice. |
response_format |
object | json_schema is validated at the gateway; one silent retry on a schema miss, then the error surfaces. |
seed |
integer | Best-effort determinism where the upstream honors it. |
Response body
{
"id": "chatcmpl-01JQ8ZT4M6",
"object": "chat.completion",
"created": 1785312000,
"model": "claude-opus-5",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hello."},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 11, "completion_tokens": 3, "total_tokens": 14}
}
Streaming
Set stream: true. The response arrives as text/event-stream with standard SSE delta chunks, and the final frame is [DONE] exactly as in the upstream schema.
from openai import OpenAI
client = OpenAI(base_url="https://api.idclinktech.com/v1", api_key="sk-idc-...")
stream = client.chat.completions.create(
model="gpt-5.6-terra",
messages=[{"role": "user", "content": "Stream a haiku about racks."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")
Wire format
data: {"id":"chatcmpl-01JQ8ZT4M6","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[{"index":0,"delta":{"content":"Cold"},"finish_reason":null}]}
data: {"id":"chatcmpl-01JQ8ZT4M6","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
Response headers are flushed before the first token, so route and rate are readable while the stream is still open.
Tool calls
Pass a tools array on the request. Tool-capable models answer with tool_calls on the assistant message instead of prose; you run the function and reply with a tool-role message carrying the same tool_call_id.
{
"model": "gpt-5.6-sol",
"messages": [
{"role": "user", "content": "How hot is rack B-14 right now?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_rack_temp",
"description": "Read the current inlet temperature for one rack.",
"parameters": {
"type": "object",
"properties": {
"rack_id": {
"type": "string",
"description": "Rack label, for example B-14."
}
},
"required": ["rack_id"]
}
}
}
],
"tool_choice": "auto"
}
Returning the result
Append the assistant message you received, then the tool result. arguments and content are both JSON-encoded strings, not objects.
{
"role": "tool",
"tool_call_id": "call_01JQ8ZT4M6",
"content": "{\"rack_id\":\"B-14\",\"celsius\":24.6}"
}
tool_choice accepts auto, none, required, or a named function. Models without tool support return 400 invalid_request rather than silently dropping the array.
Vision input
Send image parts inside the message content array. Public URLs and base64 data URIs are both accepted; vision-capable models carry the v tag in the catalog.
{
"model": "gemini-3.6-flash",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "Which PSU bay is empty?"},
{
"type": "image_url",
"image_url": {"url": "https://example.com/rack-rear.jpg"}
}
]
}
]
}
- Data URIs —
data:image/png;base64,iVBORw0KGgo…in the sameurlfield. - Limits — 20 MB per image, 8 images per message; anything larger is rejected at the gateway.
- Billing — image tokens land in
prompt_tokensat the model's input rate.
Routing tiers
Every catalog entry sits in a tier: frontier, balanced, or efficient. Naming an efficient model is the direct route; sending X-IDC-Route-Tier additionally caps what the router may fall back to, so a failover can never quietly upgrade you into a more expensive pool.
curl https://api.idclinktech.com/v1/chat/completions \
-H "Authorization: Bearer $IDCLINKTECH_API_KEY" \
-H "Content-Type: application/json" \
-H "X-IDC-Route-Tier: efficient" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Classify this ticket: disk SMART warning."}]
}'
Tier caps are enforced at dispatch. If no healthy pool fits the cap, the call fails with 503 rather than overspending.
SEC.03 — OPERATIONS
Response headers
Every response carries the route it took and the rate it was billed at, so cost attribution is a log field rather than a monthly reconciliation exercise.
| Response header | Description |
|---|---|
X-IDC-Request-Id |
Stable per-request ID. Quote it in any support thread. |
X-IDC-Route |
Resolved route as provider/region/pool. |
X-IDC-Rate-In |
The contracted input-token rate applied to this call. |
X-IDC-Rate-Out |
The contracted output-token rate applied to this call. |
X-IDC-Provider-Latency |
Upstream time to last token in ms, excluding gateway overhead. |
X-IDC-Failover |
true when the first-choice pool was skipped. |
HTTP/1.1 200 OK
Content-Type: application/json
X-IDC-Request-Id: req_01JQ8ZT4M6
X-IDC-Route: anthropic/us-west/pool-c
X-IDC-Rate-In: <contracted-rate>
X-IDC-Rate-Out: <contracted-rate>
X-IDC-Provider-Latency: 1184
X-IDC-Failover: false
Traces are exported to your own sink — a signed webhook, newline-delimited JSON on a prefix you own, or OTLP to a collector you specify. There is no dashboard you are required to live in.
Errors & retries
Standard HTTP status codes with a typed body. 429 and 5xx carry Retry-After; honor it and back off exponentially. Upstream failovers happen below your client and never consume your retry budget.
| Status | Type | Cause |
|---|---|---|
400 |
invalid_request | Validation failed at the gateway; no upstream was touched. |
401 |
invalid_api_key | Missing, malformed, or revoked key. |
402 |
payment_required | Trial credit or prepaid balance exhausted. |
404 |
model_not_found | Unknown API ID, or an alias window that has closed. |
429 |
rate_limit_exceeded | Project ceiling hit. Retry after the header value. |
503 |
upstream_unavailable | Every healthy pool for that model is saturated. Rare. |
{
"error": {
"type": "rate_limit_exceeded",
"message": "Project token budget exhausted for the current minute.",
"code": 429,
"request_id": "req_01JQ8ZT4M6"
}
}
Rate limits
Limits are per project and counted on a rolling minute. Ceilings below are defaults; committed traffic gets reserved capacity that you rebalance between projects from the dashboard or the management API.
| Plan | Requests / min | Tokens / min | Concurrent streams |
|---|---|---|---|
| Developer | 10,000 | 2,000,000 | 64 |
| Scale | 60,000 | 20,000,000 | 512 |
| Enterprise | Reserved | Reserved | Negotiated |
Crossing a ceiling returns 429 with Retry-After. It never queues silently — latency you cannot see is worse than an error you can.
SEC.04 — SDKs
Python
Use the official openai package and point base_url at the gateway. Nothing else changes: pip install openai, then swap two lines.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.idclinktech.com/v1",
api_key=os.environ["IDCLINKTECH_API_KEY"],
)
resp = client.chat.completions.create(
model="claude-opus-5",
messages=[{"role": "user", "content": "Say hello."}],
)
print(resp.choices[0].message.content)
The streaming form is shown in DOC 02.2. Provider-native SDKs are not required for any catalog model — one client covers all thirty-six.
TypeScript / Node
Same package, same schema: pnpm add openai, then set baseURL. Types come from the package, so tool and vision payloads stay checked at compile time.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.idclinktech.com/v1",
apiKey: process.env.IDCLINKTECH_API_KEY,
});
const resp = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Say hello." }],
});
console.log(resp.choices[0].message.content);
cURL
For streaming from the shell, add -N so curl does not buffer the event stream.
curl -N https://api.idclinktech.com/v1/chat/completions \
-H "Authorization: Bearer $IDCLINKTECH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-terra",
"messages": [{"role": "user", "content": "Say hello."}],
"stream": true
}'
Add -D - to dump the X-IDC-* headers alongside the body while you are wiring things up.
Email sales@idclinktech.com. Technical questions are answered by the on-call engineer, not a ticket queue.