Docs
Quick start
The API is OpenAI-compatible. Send requests to https://hemmingway.io/v1 with a key from API keys and the model hemmingway-27b.
curl https://hemmingway.io/v1/chat/completions \
-H "Authorization: Bearer $HEMMINGWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "hemmingway-27b",
"messages": [{"role": "user", "content": "Hello"}]
}'
import os
from openai import OpenAI
client = OpenAI(
base_url="https://hemmingway.io/v1",
api_key=os.environ["HEMMINGWAY_API_KEY"],
)
reply = client.chat.completions.create(
model="hemmingway-27b",
messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://hemmingway.io/v1",
apiKey: process.env.HEMMINGWAY_API_KEY,
});
const reply = await client.chat.completions.create({
model: "hemmingway-27b",
messages: [{ role: "user", content: "Hello" }],
});
console.log(reply.choices[0].message.content);
The Python and JavaScript examples use OpenAI's SDKs: pip install openai or npm install openai.
Authentication
Send the key as a Bearer token in the Authorization header:
Authorization: Bearer hemmingway_live_...- Keys start with
hemmingway_live_. An account can have 10. - A key is shown once, when you make it. Hemmingway keeps only a hash of it.
- Every key spends the credit of the account that made it. Keep keys out of web pages and apps you hand out.
- Revoke a key on the API keys page. Requests with it stop working at once.
Models
There is one model, hemmingway-27b. GET /v1/models lists it.
curl https://hemmingway.io/v1/models \
-H "Authorization: Bearer $HEMMINGWAY_API_KEY"{"object": "list", "data": [{"id": "hemmingway-27b", "object": "model", "owned_by": "hemmingway"}]}Chat completions
POST /v1/chat/completions takes the body of OpenAI's Chat Completions. These fields reach the model. Any other field is dropped.
| Field | What it does |
|---|---|
messages | Required. The conversation in OpenAI's message format, with at least one message. |
model | hemmingway-27b. There is one model, and the reply names it hemmingway-27b whatever you send. |
max_tokens, max_completion_tokens | The most tokens to write, thinking included. Capped at 32,768, which is also the default. |
stream | true or false. See Streaming. |
stream_options | {"include_usage": true} adds a last chunk with the token counts. |
temperature, top_p, top_k, min_p | Sampling. |
presence_penalty, frequency_penalty, repetition_penalty | Penalties for repeating. |
stop | Text that ends the reply. |
seed | A number for repeatable sampling. |
response_format | OpenAI's response_format, passed to the model. |
tools, tool_choice, parallel_tool_calls | Function calling, in OpenAI's format. |
reasoning_effort, enable_thinking, chat_template_kwargs | Thinking. See Thinking. |
The reply is OpenAI's chat completion object. usage has the token counts, and the x-request-id header names the request.
{
"id": "...",
"object": "chat.completion",
"model": "hemmingway-27b",
"choices": [
{"index": 0, "message": {"role": "assistant", "content": "Hello. What do you need?"}, "finish_reason": "stop"}
],
"usage": {"prompt_tokens": 9, "completion_tokens": 41, "total_tokens": 50}
}Thinking
Hemmingway-one thinks before it answers, at its highest level unless you ask for less. The thinking comes in reasoning_content, apart from content: on the message, or on each streamed delta.
reasoning_effort:low,mediumorxhigh, the default.highis read asxhighandminimalaslow. Any other value is refused.enable_thinking: falseturns thinking off, at the top level of the body or inchat_template_kwargs.chat_template_kwargspasses onlyenable_thinking,reasoning_effortandpreserve_thinking. Earlier assistant messages sent back with theirreasoning_contentkeep it in the prompt. Withpreserve_thinking: false, the thinking of those before your last message is left out.- Thinking is billed as output tokens and counts toward
max_tokens.
# less thinking
curl https://hemmingway.io/v1/chat/completions \
-H "Authorization: Bearer $HEMMINGWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "hemmingway-27b", "messages": [{"role": "user", "content": "Hello"}], "reasoning_effort": "low"}'
# no thinking
curl https://hemmingway.io/v1/chat/completions \
-H "Authorization: Bearer $HEMMINGWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "hemmingway-27b", "messages": [{"role": "user", "content": "Hello"}], "enable_thinking": false}'
# less thinking
reply = client.chat.completions.create(
model="hemmingway-27b",
messages=[{"role": "user", "content": "Hello"}],
reasoning_effort="low",
)
# no thinking
reply = client.chat.completions.create(
model="hemmingway-27b",
messages=[{"role": "user", "content": "Hello"}],
extra_body={"enable_thinking": False},
)
// less thinking
const reply = await client.chat.completions.create({
model: "hemmingway-27b",
messages: [{ role: "user", content: "Hello" }],
reasoning_effort: "low",
});
// no thinking
const quick = await client.chat.completions.create({
model: "hemmingway-27b",
messages: [{ role: "user", content: "Hello" }],
enable_thinking: false,
});
Streaming
With "stream": true the reply comes as server-sent events: data: lines with chat completion chunks, then data: [DONE].
- With
"stream_options": {"include_usage": true}, a last chunk hasusageand no choices. - While nothing else comes, a
: keep-alivecomment line comes every 15 seconds. OpenAI's SDKs skip it. - A stream that sends nothing for 5 minutes is cut.
curl -N https://hemmingway.io/v1/chat/completions \
-H "Authorization: Bearer $HEMMINGWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "hemmingway-27b",
"messages": [{"role": "user", "content": "Hello"}],
"stream": true,
"stream_options": {"include_usage": true}
}'
stream = client.chat.completions.create(
model="hemmingway-27b",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n", chunk.usage)
const stream = await client.chat.completions.create({
model: "hemmingway-27b",
messages: [{ role: "user", content: "Hello" }],
stream: true,
stream_options: { include_usage: true },
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
if (chunk.usage) console.log("\n", chunk.usage);
}
data: {"id":"...","object":"chat.completion.chunk","model":"hemmingway-27b","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: {"id":"...","object":"chat.completion.chunk","model":"hemmingway-27b","choices":[{"index":0,"delta":{"content":". What do you need?"},"finish_reason":"stop"}]}
data: {"id":"...","object":"chat.completion.chunk","model":"hemmingway-27b","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":41,"total_tokens":50}}
data: [DONE]Errors
An error answers with its HTTP status and a body like this. The message is written for people.
{"error": {"code": "bad_request", "message": "messages must be a list with at least one message."}}| Status | Code | When |
|---|---|---|
| 400 | bad_request | The body isn't a JSON object, messages is missing or empty, stream isn't true or false, or the model couldn't take the request. |
| 401 | bad_key | The key is unknown or revoked. |
| 401 | signed_out | No key was sent, or it doesn't start with hemmingway_live_. |
| 402 | out_of_credit | The account's credit is used up. Add credit on the Billing page. |
| 403 | not_in_api | A key was sent to /v1/tools/. Web search is part of the Hemmingway app. |
| 404 | not_found | There is nothing at that address. |
| 413 | bad_request | The body is over 4 MB. |
| 429 | busy | The account already has 8 requests running. |
| 500 | server_error | Something went wrong on Hemmingway's server. The error's rid names the request. |
| 502 | model_unreachable, model_error | The model didn't answer, or answered with an error. Try again in a minute. |
Limits
- 8 requests at once per account, across all its keys.
max_tokensup to 32,768.- A request body up to 4 MB.
- A request runs for 13 minutes at most. A stream that sends nothing for 5 minutes is cut.
- 10 keys per account.
- The model only. Web search and page reading are part of the Hemmingway app.
Pricing
| Tokens | Per million |
|---|---|
| Input | $0.50 |
| Output, thinking included | $2.50 |
- A request is charged when it ends, from the token counts the model reports. A reply cut off before its counts is counted at about 4 characters a token.
- A request the model never got costs nothing.
- Each request's charge is rounded up to the next millionth of a dollar.
- Requests are let in while the credit is above zero, so requests running at once can take it a little below zero.
Credit
- The API is paid from prepaid credit. Add it on the Billing page: $5, $20, $50, $100 or any amount from $5. Prices include tax.
- Credit lasts 12 months from when you buy it. The oldest credit is used first, and what is left of a purchase after 12 months expires.
- Credit isn't refunded, except where the law requires it. See the Terms.