curl --request POST \
--url https://api.hopscotchlabs.ai/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "openai/gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Say hi in five words."
}
]
}
'import requests
url = "https://api.hopscotchlabs.ai/v1/chat/completions"
payload = {
"model": "openai/gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Say hi in five words."
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
model: 'openai/gpt-4o-mini',
messages: [{role: 'user', content: 'Say hi in five words.'}]
})
};
fetch('https://api.hopscotchlabs.ai/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hopscotchlabs.ai/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => 'openai/gpt-4o-mini',
'messages' => [
[
'role' => 'user',
'content' => 'Say hi in five words.'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hopscotchlabs.ai/v1/chat/completions"
payload := strings.NewReader("{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Say hi in five words.\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hopscotchlabs.ai/v1/chat/completions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Say hi in five words.\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hopscotchlabs.ai/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Say hi in five words.\"\n }\n ]\n}"
response = http.request(request)
puts response.read_bodyCreate a chat completion
Send an OpenAI chat completion request. Set stream: true for server-sent events.
Credit is held when the request is admitted and settled against the provider’s own token counts when it finishes. You are charged the provider’s rate at face value. Nothing is added per request.
curl --request POST \
--url https://api.hopscotchlabs.ai/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "openai/gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Say hi in five words."
}
]
}
'import requests
url = "https://api.hopscotchlabs.ai/v1/chat/completions"
payload = {
"model": "openai/gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Say hi in five words."
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
model: 'openai/gpt-4o-mini',
messages: [{role: 'user', content: 'Say hi in five words.'}]
})
};
fetch('https://api.hopscotchlabs.ai/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hopscotchlabs.ai/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => 'openai/gpt-4o-mini',
'messages' => [
[
'role' => 'user',
'content' => 'Say hi in five words.'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hopscotchlabs.ai/v1/chat/completions"
payload := strings.NewReader("{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Say hi in five words.\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hopscotchlabs.ai/v1/chat/completions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Say hi in five words.\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hopscotchlabs.ai/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Say hi in five words.\"\n }\n ]\n}"
response = http.request(request)
puts response.read_bodyAuthorizations
A workspace API key in an Authorization: Bearer header. A missing header, a scheme other than Bearer, a key of the wrong shape, and a key we reject are all 401.
Body
An OpenAI chat completion request. Fields beyond the ones named here are forwarded to the provider unchanged rather than refused for being unfamiliar.
The model to call, in one of exactly two forms. A provider-pinned id, "{provider}/{that provider's own id}", is split once on the FIRST slash: the part before it names one of the provider accounts this platform serves, and everything after it is that provider's own id for the model, passed on verbatim, slashes included. A workspace routing-profile slug, "profile/{identifier}", is the other form: the platform then tries that profile's models in the customer's own order, and the usage record and the charge name whichever model actually served. A bare model id is refused with 400 and the code "model_id_not_pinned". GET /v1/models lists the exact strings this key can call, one per model and provider that serves it. A third string is accepted alongside them: "hopscotch/auto", which names whichever routing profile the workspace made its default. It is a fixed id every workspace can send, so a client is configured once and the route behind it is changed later on the Routing screen without touching the calling code. It resolves through the same path a profile slug takes, so the usage record and the charge name whichever model actually served, and a workspace with no default route set is refused 404 "model_not_available" saying exactly that.
"openai/gpt-4o-mini"
The most output tokens the reply may use. Optional, as it is on OpenAI. Left out, it is filled in for the providers whose own API refuses a request that does not carry one, and the figure used is the model's published max_output_tokens, which GET /v1/models/{model} reports and which is what credit is held for on a request naming no ceiling. Every other provider is sent the request unchanged. max_completion_tokens counts as this field being given.
256
Response
The completion. A response that is not streamed carries the provider's token counts in its own usage object. Its model field echoes the slug you asked for rather than the provider's internal name, except in three cases that are returned as the provider sent them: a streamed reply, whose frames are passed through unchanged; a request naming a routing profile, which names a chain rather than one model; and a successful body carrying no string model field at all.
When stream is true the body is text/event-stream instead, and the counts arrive on a :x-hopscotch-usage comment line immediately before data: [DONE]; the server-sent events specification requires parsers to ignore comment lines, so an SDK never sees it.