01
Quick start
Three steps to your first request. If your code already uses the OpenAI SDK, change only the base URL and the key.
- 01
Get credits
Buy credits with USDG or deposit USDG in the Drip app. Requests are paid from this balance.
- 02
Create an API key
Open the console and create a key. It is shown once, so store it right away, for example as
DRIP_API_KEY. - 03
Send a request
Use the base URL below with any OpenAI SDK, or plain HTTP.
https://usedrip.lat/v1curl https://usedrip.lat/v1/chat/completions \
-H "Authorization: Bearer $DRIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-5.5",
"messages": [{"role": "user", "content": "Say hello in five words."}]
}'anthropic/claude-haiku-5.5 is the model ID for Claude Haiku 5.5, the one model Drip serves. See Models for its prices and limits.
02
Authentication
Send your key as a bearer token in the Authorization header of every chat completion request. Listing models needs no key.
Authorization: Bearer drip_sk_…- Keys start with
drip_sk_, followed by 43 random characters. - Drip stores only a SHA-256 hash of the key and its last four characters. A lost key cannot be shown again; rotate it instead.
- Each key belongs to one Drip account, and all its requests are paid from that account’s credits.
- The API sends CORS headers, so a browser can call it. Still, keep keys on your server: anyone who can read a key can spend your credits.
| Status | Code | What happened |
|---|---|---|
| 401 | missing_api_key | No Authorization header was sent. |
| 401 | invalid_api_key | The key is malformed or does not exist. |
| 401 | revoked_api_key | The key was revoked or rotated. Use the new key or create one. |
03
API keys
Create and manage keys in the console.
| Setting | What it does |
|---|---|
| Name | A label of 1 to 60 characters. It appears in the console and in error messages. |
| Rate limit | Fixed at 60 requests per minute for every key. See Rate limits. |
Actions
| Action | What happens |
|---|---|
| Create | Returns the full key once. An account can have up to 20 active keys. |
| Rotate | Issues a new key with the same name and settings, and revokes the old one at once. |
| Revoke | The key stops working from its next request. |
- The console also has usage by key, model and day.
04
Models
Drip serves one model, Claude Haiku 5.5, with the ID anthropic/claude-haiku-5.5. Model IDs have the form provider/model. The models endpoint lists them and needs no key.
/v1/modelsno key needed/v1/models/{id}404 model_not_found if unknown# No API key needed
curl https://usedrip.lat/v1/models
# One model
curl https://usedrip.lat/v1/models/anthropic/claude-haiku-5.5Each model object has OpenAI’s shape plus a drip field with prices, limits, capabilities and fallbacks.
{
"id": "anthropic/claude-haiku-5.5",
"object": "model",
"created": 1760000000,
"owned_by": "anthropic",
"drip": {
"name": "Claude Haiku 5.5",
"provider": "Anthropic",
"pricing": {
"currency": "USD",
"unit": "1M tokens",
"input": 0.105,
"output": 0.525,
"includes_service_margin": 0.05,
"placeholder": false
},
"context_window": 64000,
"max_output_tokens": 32000,
"capabilities": ["text", "vision", "files", "tools", "json", "reasoning"],
"good_for": "Fast, capable answers, writing, coding help, documents and images.",
"fallbacks": []
}
}Current model
Prices are in USD per 1M tokens and already include Drip’s 5% service margin.
| Model ID | Context | Max output | Capabilities | Input / 1M | Output / 1M |
|---|---|---|---|---|---|
anthropic/claude-haiku-5.5 | 64K | 32K | text, vision, files, tools, json, reasoning | $0.105 | $0.525 |
| Capability | Meaning |
|---|---|
| text | Reads and writes text. |
| vision | Accepts images in messages. |
| files | Accepts file parts, such as PDFs. |
| tools | Supports tools and tool calls (function calling). |
| json | Supports JSON mode through response_format. |
| reasoning | A reasoning model, suited to multi-step problems. |
05
Chat completions
One endpoint. Requests and responses follow the OpenAI Chat Completions format.
/v1/chat/completionsContent-Type: application/jsonRequest body
| Field | Notes |
|---|---|
model | Required. anthropic/claude-haiku-5.5, or another ID from /v1/models. |
messages | Required and non-empty. Each message has a role of system, developer, user, assistant or tool. |
stream | Set to true for server-sent events. See Streaming. |
max_tokens | Or max_completion_tokens. A positive integer; default 4,096. Drip lowers it to the model’s maximum output and to what your balance covers. |
tools | With tool_choice and parallel_tool_calls. tool_choice of required or a named tool is asked for in the prompt rather than forced. |
response_format | json_schema makes the reply follow your schema. json_object asks the model for a single JSON object. |
reasoning_effort | low, medium or high: how much the model thinks before it answers. Default low. |
stop | A string or up to four strings. The reply ends before any of them. |
| Sampling and more | temperature, top_p, seed, presence_penalty, frequency_penalty, logit_bias, logprobs and top_logprobs are accepted but not used: Claude Haiku 5.5 uses its own default sampling. |
n | Only 1 is supported. Any other value fails with 400 unsupported_parameter. |
Fields not in this table, including user, are dropped before the request reaches the model.
Response
The standard completion object. model is the model that actually answered. An extra drip field holds the request ID, the model you asked for, any fallback, and what the request cost.
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1761040000,
"model": "anthropic/claude-haiku-5.5",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hello there, nice to meet you!" },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 14, "completion_tokens": 8, "total_tokens": 22 },
"drip": {
"request_id": "req_3f9c…",
"model": "anthropic/claude-haiku-5.5",
"requested_model": "anthropic/claude-haiku-5.5",
"fallback_from": null,
"cost_micros": 6,
"cost_usd": 0.000006
}
}Response headers
| Header | Meaning |
|---|---|
x-drip-request-id | The ID of this request. |
x-drip-model | The model that answered. |
x-drip-fallback-from | Only when a fallback answered: the model you asked for. |
x-ratelimit-limit-requests | The key's requests per minute. |
x-ratelimit-remaining-requests | Requests left in the current minute. |
x-ratelimit-reset-requests | Time until the minute resets, such as 12s. |
raw = client.chat.completions.with_raw_response.create(
model="anthropic/claude-haiku-5.5",
messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(raw.headers.get("x-drip-model"), raw.headers.get("x-ratelimit-remaining-requests"))
reply = raw.parse()
drip = getattr(reply, "drip", None) # extra field: model, cost_usd, request_id
print(reply.choices[0].message.content, drip)Fallbacks
Each model lists fallback models (fallbacks in /v1/models). Claude Haiku 5.5 has none today, so if Anthropic fails or times out, the request fails with 503 all_upstreams_failed and you are not charged for it. When a model has fallbacks, Drip tries the next one your key may use, you pay for the model that answered, and x-drip-fallback-from shows that a fallback was used.
If the provider rejects the request itself (a 4xx other than 408, 409, 425 or 429), Drip returns that error with code upstream_rejected and does not try a fallback.
06
Streaming
Set stream: true to receive server-sent events while the model writes. The SDKs read them for you.
stream = client.chat.completions.create(
model="anthropic/claude-haiku-5.5",
messages=[{"role": "user", "content": "Write a haiku about rain."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
# The last chunk carries usage and a "drip" field with the model that answered and the cost.After the last content chunk, Drip always sends one more chunk with empty choices, the token usage and the drip field, then data: [DONE]. You do not need stream_options to get usage.
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"anthropic/claude-haiku-5.5","choices":[{"index":0,"delta":{"content":" rain."}}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"anthropic/claude-haiku-5.5","choices":[],"usage":{"prompt_tokens":15,"completion_tokens":19,"total_tokens":34},"drip":{"request_id":"req_…","model":"anthropic/claude-haiku-5.5","requested_model":"anthropic/claude-haiku-5.5","fallback_from":null,"cost_micros":12,"cost_usd":0.000012}}
data: [DONE]- If the model stops partway, the stream ends with an error event with code
stream_interrupted. You pay only for the tokens received. - If you close the connection early, Drip stops the model and charges for the tokens generated until then.
- Drip waits for the first chunk before it starts the stream, so a provider that fails before answering returns a normal error instead of a broken stream.
07
Tools, JSON and images
These work as in the OpenAI API, and Claude Haiku 5.5 supports all three.
Tool calls
Pass tools and read tool_calls from the reply. Run the tool yourself, then send its result back in a message with role tool.
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
reply = client.chat.completions.create(
model="anthropic/claude-haiku-5.5",
messages=[{"role": "user", "content": "Is it raining in Lisbon?"}],
tools=tools,
)
call = reply.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)JSON mode
Set response_format to {"type": "json_object"} and ask for JSON in the prompt.
import json
reply = client.chat.completions.create(
model="anthropic/claude-haiku-5.5",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Reply with JSON only."},
{"role": "user", "content": "List three fruits with their colours."},
],
)
data = json.loads(reply.choices[0].message.content)Images and files
Send images as image_url content parts, as an https URL or a base64 data URL. Send PDFs and text files as file parts with a base64 file_data URL. For the up-front credit check, each image or file part counts as about 1,500 tokens. You are billed for the tokens the model reports.
reply = client.chat.completions.create(
model="anthropic/claude-haiku-5.5", # needs "vision" in its capabilities
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What is in this picture?"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
],
}],
)08
Billing and credits
Every request is paid from your account's credit balance. Credits are a dollar balance that can be spent only on AI through Drip.
Where credits come from
| Source | How it works |
|---|---|
| Interest | Deposit USDG in a Drip vault. Harvested interest is added as credits, minus a 10% performance fee (proposed). |
| Buy with USDG | Pay USDG in the app; credits are added once the transaction confirms. A small purchase margin is still to be set. |
What a request costs
Cost = prompt tokens × input price + completion tokens × output price, at the prices of the model that answered. Prices include the service margin (currently 5%; the proposed range is 3–8%). Costs are counted in millionths of a dollar and rounded up.
How charging works
- 01
Hold
Before calling the model, Drip holds the most the request could cost: the prompt plus max_tokens of output, at the model's prices.
- 02
Fit to your balance
If your balance is lower than that, Drip lowers max_tokens to what it covers. If it covers fewer than 16 output tokens, the request fails with 402 insufficient_credits.
- 03
Settle
After the reply, Drip charges the real cost from the token usage and releases the rest of the hold.
- See each request’s cost in
drip.cost_usdin the response, and totals by key, model and day in the console. - Unused credits never expire. They cannot be withdrawn as cash.
- All keys of an account share one balance.
09
Agent wallets
An agent wallet will be a contract on Robinhood Chain that you own. It holds USDG for one AI agent, and the agent can use it to buy Drip credits on its own, within caps you set.
10
Errors
Errors use OpenAI’s format, so the SDKs raise them as usual. Check code to decide what to do; message explains the problem in plain words.
{
"error": {
"message": "Not enough credits for this request. Your available balance is $0.000012; this request needs at least $0.000041. Buy credits or deposit USDG in the Drip app.",
"type": "insufficient_quota",
"param": null,
"code": "insufficient_credits"
}
}| Status | Type | Code | What to do |
|---|---|---|---|
| 400 | invalid_request_error | invalid_json | Send a valid JSON body. |
| 400 | invalid_request_error | invalid_body | The body must be a JSON object. |
| 400 | invalid_request_error | missing_model | Add a model ID. |
| 400 | invalid_request_error | invalid_messages | Send a non-empty messages array with valid roles. |
| 400 | invalid_request_error | invalid_parameter | max_tokens must be a positive integer. |
| 400 | invalid_request_error | unsupported_parameter | Remove n or set it to 1. |
| 400 | invalid_request_error | unsupported_input | Send files inline as file.file_data (a base64 data URL). |
| 400 | invalid_request_error | context_length_exceeded | Shorten the prompt. Claude Haiku 5.5 takes up to 64K tokens. |
| 401 | authentication_error | missing_api_key | Send Authorization: Bearer drip_sk_… |
| 401 | authentication_error | invalid_api_key | Check the key. Create a new one if it is lost. |
| 401 | authentication_error | revoked_api_key | Switch to the rotated key or create a new one. |
| 402 | insufficient_quota | insufficient_credits | Add credits, then retry. |
| 404 | not_found_error | model_not_found | Use an ID from /v1/models. |
| 404 | not_found_error | unknown_endpoint | Only /v1/models and /v1/chat/completions exist. |
| 405 | invalid_request_error | method_not_allowed | Use POST for chat completions. |
| 413 | invalid_request_error | request_too_large | Keep the request body under 64 MB: send smaller or fewer files. |
| 429 | rate_limit_error | rate_limit_exceeded | Wait retry-after seconds, then retry. |
| 4xx | upstream_error | upstream_rejected | The model provider refused the request. Read the message and fix the request. |
| In stream | upstream_error | stream_interrupted | The model stopped partway. Retry if you need the full reply. |
| 500 | server_error | internal_error | Retry. If it keeps happening, contact Drip. |
| 502 | api_error | upstream_unavailable | Drip's API did not answer. Retry shortly. |
| 503 | upstream_error | all_upstreams_failed | The model provider is down. Retry after retry-after. |
| 503 | upstream_error | upstream_not_configured | Drip cannot reach its model provider right now. Retry after retry-after. |
| 503 | api_error | unavailable | Drip's API is unavailable right now. Retry shortly. |
import openai
try:
reply = client.chat.completions.create(
model="anthropic/claude-haiku-5.5",
messages=[{"role": "user", "content": "Hello"}],
)
except openai.APIStatusError as e:
print(e.status_code, e.code, e.message)
if e.code == "insufficient_credits":
... # top up credits, then retry
elif e.code == "key_monthly_limit_reached":
... # stop: this key is done until next month or a higher limit
elif e.code == "rate_limit_exceeded":
... # wait retry-after seconds: e.response.headers["retry-after"]SDK retries
11
Rate limits
Each API key has its own limit of 60 requests per minute.
| Rule | Value |
|---|---|
| Limit | 60 requests per minute per key. Setting it per key comes in Phase 2. |
| Window | Fixed one-minute windows that start on the clock minute |
| Counted on | POST /v1/chat/completions. Listing models is not limited. |
| Over the limit | 429 rate_limit_exceeded, with a retry-after header in seconds |
Completion responses carry x-ratelimit-limit-requests, x-ratelimit-remaining-requests and x-ratelimit-reset-requests, so you can slow down before you reach the limit.
HTTP/1.1 429 Too Many Requests
retry-after: 12
x-ratelimit-limit-requests: 60
x-ratelimit-remaining-requests: 0
x-ratelimit-reset-requests: 12s
{"error":{"message":"Rate limit reached for API key 'research agent' (ending AbCd): 60 requests per minute. Try again in 12 s.","type":"rate_limit_error","param":null,"code":"rate_limit_exceeded"}}