GoalfinderBack to home

API Reference

Last updated: 23 September 2026

Goalfinder is an OpenAI-compatible gateway. Point any OpenAI SDK at our base URL, use your Goalfinder key, and call every model in the catalog through one endpoint.

Quickstart

Create an API key in your dashboard, then make your first request. New accounts start with $1 of free credit — no card required.

Using OpenCode, Cline, or Roo Code? Start at /docs/clients for copy-paste configs.

shell
curl https://www.goalfinder.space/api/v1/chat/completions \
  -H "Authorization: Bearer $GOALFINDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash",
    "messages": [{ "role": "user", "content": "Hello!" }]
  }'

Authentication

Authenticate with a bearer token. Every request must carry your key; keys are shown once at creation and stored only as one-way hashes, so we cannot recover a lost key — revoke it and issue a new one.

header
Authorization: Bearer gf-sk-...

Requests without a valid key return 401. Keep keys server-side: never ship one in a browser bundle, mobile app or public repository.

Endpoints

All endpoints are relative to https://www.goalfinder.space/api/v1.

MethodPathDescription
POST/api/v1/chat/completionsChat completion, streaming or non-streaming.
GET/api/v1/modelsList active models with their per-1M-token pricing.
OpenAI SDK drop-in. Because the surface is OpenAI-compatible, you only change the base URL and the key. Set base_url to https://www.goalfinder.space/api/v1. Note that the base URL already includes the /api/v1 prefix — do not append /v1 again.

Python

The official OpenAI Python SDK works unmodified. Install it with pip install openai.

python
from openai import OpenAI

client = OpenAI(
    base_url="https://www.goalfinder.space/api/v1",
    api_key="gf-sk-...",
)

resp = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Explain caching in one sentence."},
    ],
)

print(resp.choices[0].message.content)
print(resp.usage.total_tokens)

Node.js

The same applies to the official openai npm package.

typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://www.goalfinder.space/api/v1",
  apiKey: process.env.GOALFINDER_API_KEY,
});

const resp = await client.chat.completions.create({
  model: "deepseek-v4.1-flash",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Explain caching in one sentence." },
  ],
});

console.log(resp.choices[0].message.content);
console.log(resp.usage?.total_tokens);

Streaming

Pass stream: true to receive server-sent events. Each chunk carries choices[0].delta.content, and the stream ends with the literal terminator data: [DONE].

python
stream = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Count to five."}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
typescript
const stream = await client.chat.completions.create({
  model: "deepseek-v4.1-flash",
  messages: [{ role: "user", content: "Count to five." }],
  stream: true,
});

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content;
  if (delta) process.stdout.write(delta);
}

Usage is settled when the stream finishes. If a client disconnects early, we settle for the tokens already delivered rather than the whole request.

Tool calling

Function/tool calling passes straight through to the upstream model. Declare tools in the standard OpenAI shape and the model will return tool_calls that you execute and feed back as a role: "tool" message.

python
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

resp = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Weather in Toronto?"}],
    tools=tools,
    tool_choice="auto",
)

call = resp.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)  # get_weather {"city":"Toronto"}

# Run your function, then send the result back.
resp2 = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[
        {"role": "user", "content": "Weather in Toronto?"},
        resp.choices[0].message,
        {"role": "tool", "tool_call_id": call.id, "content": "18C, clear"},
    ],
    tools=tools,
)
print(resp2.choices[0].message.content)
typescript
const tools = [{
  type: "function" as const,
  function: {
    name: "get_weather",
    description: "Get the current weather for a city.",
    parameters: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  },
}];

const resp = await client.chat.completions.create({
  model: "deepseek-v4.1-flash",
  messages: [{ role: "user", content: "Weather in Toronto?" }],
  tools,
  tool_choice: "auto",
});

const call = resp.choices[0].message.tool_calls![0];
console.log(call.function.name, call.function.arguments);
Billing note. Tool calls are billed like any other completion, by tokens. When a model replies with tool calls instead of prose, the generation's output is carried in tool_calls rather than content. If you are reconciling your balance against usage, prefer the usage object on the response over counting characters. Long tool-call loops can consume credit quickly — keep tool_choice and iteration caps tight.

Error codes

Errors use the OpenAI envelope. Check error.type for programmatic handling and error.code for the specific case.

json
{
  "error": {
    "message": "Invalid or revoked API key.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
StatusCodeMeaning
400invalid_request_errorMalformed JSON body or missing a required field such as model.
401missing_api_keyNo Authorization header was sent.
401invalid_api_keyThe key is wrong or has been revoked.
402insufficient_balanceYour balance will not cover the worst-case cost of this request. Buy a pack to continue.
404model_not_foundThe model does not exist or is not active. Check GET /api/v1/models.
429rate_limit_exceededToo many requests. No RPM quota is published — see Rate limits below. Back off and retry.
500configuration_errorA required upstream endpoint is not configured. Contact support.
502upstream_errorThe model provider failed or was unreachable. Retry; you are not billed for a request that never ran.
503upstream_errorThe provider is overloaded with no available channel. Retry with backoff.

A 402 is returned before any upstream call is made, so a rejected request never costs you anything. Retry 429 and 5xx responses with exponential backoff and jitter.

Not in v1

This gateway implements the OpenAI-compatible Chat Completions surface, and nothing else. Being explicit about what is absent is deliberate — so you can design around it rather than discover it in production. Everything below returns 404 (no such route) rather than 501.

Not supportedEndpointNotes
EmbeddingsPOST /v1/embeddingsNo vector endpoints at all. Use a dedicated embedding provider, or have the model return vectors as text via response_format.
Image generationPOST /v1/images/generationsWe do not generate images.
Image input (vision)POST /v1/chat/completionsThe catalog is text-only. Message content must be a string — an array of content blocks is rejected. Do not send base64 image parts.
Anthropic MessagesPOST /v1/messagesNot exposed. Everything goes through Chat Completions, including the Qwen models.
OpenAI Responses APIPOST /v1/responsesNot exposed. Use Chat Completions; there is no stateful conversation object.
Web searchweb_search_optionsNot supported. The upstream may reject the parameter rather than ignore it.
Extended thinkingthinkingNot configurable. Reasoning tokens are not billed separately, and are not returned as a distinct field.
Assistants, Batches, Files, Fine-tuning, AudiovariousNot implemented.

Tool calling is supported. The tools and tool_choice parameters are passed through, function calls are returned normally, and streaming emits delta.tool_calls incrementally as you would expect. A response that is only a tool call is billed normally — the payload counts toward completion tokens exactly like assistant text.

Measurement comes from the upstream usage report whenever the provider sends one, which is the normal case. If a provider omits it, we fall back to estimating completion tokens from the returned payload, including tool-call arguments. That fallback is approximate by nature, so if you ever see a token count that looks off against your own accounting, tell us and we will reconcile it.

Rate limits

There is no fixed request-per-minute quota published or enforced at the gateway today. Throughput is bounded by two things instead:

  • Your balance. Each request is pre-authorised against its worst-case cost, and a request we cannot afford returns 402.
  • Upstream capacity. Our providers impose their own limits, which they do not publish and which we cannot increase. Under load you may see 429 or 503 passed through from upstream.

Because no RPM is published, do not assume one. Handle 429 and 503 with exponential backoff and jitter, and treat concurrency as best-effort. If you need a committed throughput number for a production workload, contact us before you build on the assumption.

Model matrix

The catalog below is read live from production. Prices are per 1M tokens, in US dollars, at the rates you are billed.

The catalog is unavailable right now. Call GET /api/v1/models for the live list.

Chat completions parameters

ParameterTypeRequiredDescription
modelstringYesID of the model to use.
messagesarrayYesConversation history. Roles: system, user, assistant, tool.
streambooleanNoSend partial deltas as server-sent events. Default false.
max_tokensintegerNoMaximum tokens to generate. Also caps the worst-case cost we pre-authorise.
temperaturenumberNoSampling temperature, 0 to 2. Default 1.
top_pnumberNoNucleus sampling mass. Default 1.
stopstring | string[]NoUp to 4 sequences that halt generation.
nintegerNoHow many choices to generate. Default 1.
frequency_penaltynumberNo-2.0 to 2.0. Penalise repeated tokens. Default 0.
presence_penaltynumberNo-2.0 to 2.0. Penalise tokens seen so far. Default 0.
seedintegerNoDeterministic sampling, where the model supports it.
response_formatobjectNo{ type: "text" } or { type: "json_object" }.
toolsarrayNoTools the model may call. See Tool calling.
tool_choicestring | objectNo"auto", "none", "required", or a specific function.
userstringNoOpaque end-user identifier for abuse monitoring.

How billing works

  • Usage is metered per token against your prepaid balance, using the per-model rates in the matrix above.
  • Before calling upstream we compute the worst-case cost of the request from your prompt size and max_tokens. If your balance will not cover it, the request is rejected with a 402 and nothing is sent upstream.
  • We settle the actual cost once the response completes, from the usage the provider reports.
  • Cached input tokens are billed at the lower cache-read rate shown in the matrix, which makes repeated prompts with a stable prefix cheaper.
  • New accounts receive $1 of free credit to try the API. It is a one-time promotional grant per person and is not a purchase.