API reference
Anthropic-compatible API
https://fullsendai.com.Overview#
| Base URL | https://fullsendai.com (the SDKs add /v1/messages) |
|---|---|
| Authentication | x-api-key: $FULLSEND_API_KEY or Authorization: Bearer. anthropic-version and anthropic-beta are accepted; any value works. |
| Endpoints | POST /v1/messages, POST /v1/messages/count_tokens, GET /v1/models |
| Errors | Anthropic's error shape and types. See Errors. |
Create a message#
POST/v1/messages
Request body
modelstringrequired- A FullsendAI model ID from Models, not a Claude model name.
max_tokensintegerrequired- The most tokens to generate, at least 1. Lowered to the model's maximum output if higher.
messagesarrayrequired- Alternating
userandassistantturns;contentis a string or an array of content blocks. systemstring | array- Instructions for the whole conversation: a string or an array of text blocks.
temperature, top_pnumber- Sampling controls, passed to the model.
stop_sequencesstring[]- Text that ends the answer. A match ends it with
stop_reason: end_turn. streamboolean- Send Anthropic's server-sent events. See Streaming.
toolsarray- Custom tools:
name,description,input_schema. See Tool use. tool_choiceobject{"type": "auto"},any,none, or{"type": "tool", "name": "…"}.
metadata and top_k are accepted and ignored; thinking and cache_control are supported. See What's different.
curl https://fullsendai.com/v1/messages \ -H "x-api-key: $FULLSEND_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-v4.1-flash", "max_tokens": 2048, "system": "You are a senior Rust engineer. Answer with code first.", "messages": [{"role": "user", "content": "Write a lock-free ring buffer"}] }'{ "id": "msg_af2452aa21131e20bbdab2c9", "type": "message", "role": "assistant", "model": "deepseek-v4.1-flash", "content": [{ "type": "text", "text": "use std::sync::atomic::{AtomicUsize, Ordering}; …" }], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 31, "output_tokens": 642 }}Content blocks#
textuser, assistant{"type": "text", "text": "…"}imageuser{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "…"}}, or asourceof{"type": "url", "url": "https://…"}. For models that read images; see Images.tool_useassistant- A call the model made:
id,name,input. Send it back unchanged in the history. tool_resultusertool_use_id,content(a string or text blocks) and optionalis_error.
Images inside a tool_result and thinking blocks in the history are dropped before the model sees them.
The response#
idstring- Unique per response, like
msg_…. modelstring- The model ID you sent.
contentarraytextblocks, thentool_useblocks when the model called tools.stop_reasonstringend_turn(finished, or hit a stop sequence),max_tokensortool_use.stop_sequencenull- Always null.
usageobjectinput_tokensandoutput_tokens, exactly as billed.
Streaming#
Events arrive in Anthropic's order: message_start, ping, then for each block content_block_start, content_block_delta (text_delta or input_json_delta) and content_block_stop, then message_delta with the stop reason and the token counts, and message_stop. The SDKs' stream helpers work as they do against Anthropic.
event: message_startdata: {"type":"message_start","message":{"id":"msg_2976…","type":"message","role":"assistant","model":"deepseek-v4.1-flash","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}event: pingdata: {"type":"ping"}event: content_block_startdata: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}event: content_block_deltadata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"One, two,"}}event: content_block_stopdata: {"type":"content_block_stop","index":0}event: message_deltadata: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":8,"output_tokens":6}}event: message_stopdata: {"type":"message_stop"}Token counts arrive in message_delta; message_start carries zeros. If the model stops answering part way, an error event ends the stream. See Streaming.
Tool use#
The model answers with a tool_use block and stop_reason: tool_use; you run the tool and send a tool_result. Walkthrough on Tool calling.
tools = [{ "name": "run_tests", "description": "Run the project's test suite and return the output", "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]},}]question = {"role": "user", "content": "Fix the failing tests in src/parser"}first = client.messages.create(model="deepseek-v4.1-flash", max_tokens=1024, tools=tools, messages=[question])call = next(block for block in first.content if block.type == "tool_use")output = run_tests(**call.input) # your codesecond = client.messages.create(model="deepseek-v4.1-flash", max_tokens=1024, tools=tools, messages=[ question, {"role": "assistant", "content": first.content}, {"role": "user", "content": [{"type": "tool_result", "tool_use_id": call.id, "content": output}]},])Count tokens#
POST/v1/messages/count_tokens
Takes the same body as /v1/messages and returns {"input_tokens": 20}. It's an estimate (about four characters a token over the system prompt, messages and tools), free and not rate limited; billing always uses the model's exact counts.
List models#
GET/v1/models
With an anthropic-version header, the list comes back in Anthropic's shape (data[].id, display_name, created_at, has_more). See Models.
What's different from Anthropic's API#
- Model IDs are FullsendAI's (like
deepseek-v4.1-flash), not Claude names. A Claude name gets404 not_found_error. - Server tools (web search, code execution and the like) aren't run; custom tools with an
input_schemawork as usual. - Extended thinking:
thinking: {type: "enabled", budget_tokens}sets the model's reasoning effort (under 4,096 tokens is low, under 16,384 medium, otherwise high);disabledturns it off. Reasoning comes back asthinkingblocks, and asthinking_deltaevents when streaming. - Prompt caching:
cache_controlmarkers are passed to the model. Input read from the cache is reported asusage.cache_read_input_tokens(withinput_tokenscounting only the rest) and billed at the model's cached price. - Documents: base64 PDF blocks are read by models that accept files; a document given by URL, or one that isn't a PDF, gets a 400.
- Batches, Files and the Admin API aren't served.
- Errors on
GET /v1/modelsuse the OpenAI shape;/v1/messagesandcount_tokensuse Anthropic's.
SDKs#
import osimport anthropicclient = anthropic.Anthropic(base_url="https://fullsendai.com", api_key=os.environ["FULLSEND_API_KEY"])Stuck? Email [email protected]System statusChangelog