Developers

APIreference

Drip's API is OpenAI-compatible. Point an OpenAI SDK at Drip's base URL, send a Drip API key, and each request is paid from your Drip credits.

Base URLhttps://usedrip.lat/v1
Auth
Authorization: Bearer drip_sk_…
Endpoints
/v1/chat/completions · /v1/models
Works with
OpenAI SDKs (Python, JavaScript), curl, any HTTP client

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.

  1. 01

    Get credits

    Buy credits with USDG or deposit USDG in the Drip app. Requests are paid from this balance.

  2. 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.

  3. 03

    Send a request

    Use the base URL below with any OpenAI SDK, or plain HTTP.

Base URLhttps://usedrip.lat/v1
curl 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.

Header
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.
Authentication errors
StatusCodeWhat happened
401missing_api_keyNo Authorization header was sent.
401invalid_api_keyThe key is malformed or does not exist.
401revoked_api_keyThe key was revoked or rotated. Use the new key or create one.

03

API keys

Create and manage keys in the console.

API key settings
SettingWhat it does
NameA label of 1 to 60 characters. It appears in the console and in error messages.
Rate limitFixed at 60 requests per minute for every key. See Rate limits.

Actions

API key actions
ActionWhat happens
CreateReturns the full key once. An account can have up to 20 active keys.
RotateIssues a new key with the same name and settings, and revokes the old one at once.
RevokeThe key stops working from its next request.
  • The console also has usage by key, model and day.
Phase 2
Not in the console yet: per-key monthly limits, allowed models and rate limits, and sub-keys (one key per agent, each with its own budget), spend alerts for a key or the whole account, request logs and a playground. Until then, every key can spend the whole account balance, so give each app or agent its own key and revoke any key you no longer use.

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.

GET/v1/modelsno key needed
GET/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.5

Each model object has OpenAI’s shape plus a drip field with prices, limits, capabilities and fallbacks.

GET /v1/models/anthropic/claude-haiku-5.5
{
  "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 available through Drip
Model IDContextMax outputCapabilitiesInput / 1MOutput / 1M
anthropic/claude-haiku-5.564K32Ktext, vision, files, tools, json, reasoning$0.105$0.525
Capabilities
CapabilityMeaning
textReads and writes text.
visionAccepts images in messages.
filesAccepts file parts, such as PDFs.
toolsSupports tools and tool calls (function calling).
jsonSupports JSON mode through response_format.
reasoningA reasoning model, suited to multi-step problems.

05

Chat completions

One endpoint. Requests and responses follow the OpenAI Chat Completions format.

POST/v1/chat/completionsContent-Type: application/json

Request body

Request fields
FieldNotes
modelRequired. anthropic/claude-haiku-5.5, or another ID from /v1/models.
messagesRequired and non-empty. Each message has a role of system, developer, user, assistant or tool.
streamSet to true for server-sent events. See Streaming.
max_tokensOr max_completion_tokens. A positive integer; default 4,096. Drip lowers it to the model’s maximum output and to what your balance covers.
toolsWith tool_choice and parallel_tool_calls. tool_choice of required or a named tool is asked for in the prompt rather than forced.
response_formatjson_schema makes the reply follow your schema. json_object asks the model for a single JSON object.
reasoning_effortlow, medium or high: how much the model thinks before it answers. Default low.
stopA string or up to four strings. The reply ends before any of them.
Sampling and moretemperature, 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.
nOnly 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.

200 OK · application/json
{
  "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

Response headers
HeaderMeaning
x-drip-request-idThe ID of this request.
x-drip-modelThe model that answered.
x-drip-fallback-fromOnly when a fallback answered: the model you asked for.
x-ratelimit-limit-requestsThe key's requests per minute.
x-ratelimit-remaining-requestsRequests left in the current minute.
x-ratelimit-reset-requestsTime 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.

End of a stream
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

Credit sources
SourceHow it works
InterestDeposit USDG in a Drip vault. Harvested interest is added as credits, minus a 10% performance fee (proposed).
Buy with USDGPay 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

  1. 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.

  2. 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.

  3. 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_usd in 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.

Phase 2
Agent wallets arrive in Phase 2. They are not deployed on Robinhood Chain yet, so there is nothing to set up today. Until then, give each agent its own API key and pay for its requests from your account’s credits.

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.

402 Payment Required
{
  "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"
  }
}
Error codes
StatusTypeCodeWhat to do
400invalid_request_errorinvalid_jsonSend a valid JSON body.
400invalid_request_errorinvalid_bodyThe body must be a JSON object.
400invalid_request_errormissing_modelAdd a model ID.
400invalid_request_errorinvalid_messagesSend a non-empty messages array with valid roles.
400invalid_request_errorinvalid_parametermax_tokens must be a positive integer.
400invalid_request_errorunsupported_parameterRemove n or set it to 1.
400invalid_request_errorunsupported_inputSend files inline as file.file_data (a base64 data URL).
400invalid_request_errorcontext_length_exceededShorten the prompt. Claude Haiku 5.5 takes up to 64K tokens.
401authentication_errormissing_api_keySend Authorization: Bearer drip_sk_…
401authentication_errorinvalid_api_keyCheck the key. Create a new one if it is lost.
401authentication_errorrevoked_api_keySwitch to the rotated key or create a new one.
402insufficient_quotainsufficient_creditsAdd credits, then retry.
404not_found_errormodel_not_foundUse an ID from /v1/models.
404not_found_errorunknown_endpointOnly /v1/models and /v1/chat/completions exist.
405invalid_request_errormethod_not_allowedUse POST for chat completions.
413invalid_request_errorrequest_too_largeKeep the request body under 64 MB: send smaller or fewer files.
429rate_limit_errorrate_limit_exceededWait retry-after seconds, then retry.
4xxupstream_errorupstream_rejectedThe model provider refused the request. Read the message and fix the request.
In streamupstream_errorstream_interruptedThe model stopped partway. Retry if you need the full reply.
500server_errorinternal_errorRetry. If it keeps happening, contact Drip.
502api_errorupstream_unavailableDrip's API did not answer. Retry shortly.
503upstream_errorall_upstreams_failedThe model provider is down. Retry after retry-after.
503upstream_errorupstream_not_configuredDrip cannot reach its model provider right now. Retry after retry-after.
503api_errorunavailableDrip'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

The OpenAI SDKs retry 429 and 5xx responses twice by default.

11

Rate limits

Each API key has its own limit of 60 requests per minute.

Rate limit rules
RuleValue
Limit60 requests per minute per key. Setting it per key comes in Phase 2.
WindowFixed one-minute windows that start on the clock minute
Counted onPOST /v1/chat/completions. Listing models is not limited.
Over the limit429 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.

429 Too Many Requests
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"}}

Next

Get your API key.

Create, rotate and revoke keys, and see usage by key, model and day in the console.