Quickstart: Python

Two ways to call the API from Python 3.9 or later: plain HTTP with requests, or the openai library pointed at Syntic’s base URL. Both are shown side by side; pick one.

Create a project

mkdir syntic-hello && cd syntic-hello
python3 -m venv .venv && source .venv/bin/activate
pip install requests openai

Send a request

# hello.py
import os
 
import requests
 
resp = requests.post(
    "https://app.syntic.ai/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['SYNTIC_API_KEY']}"},
    json={
        "model": "Amara S1.2",
        "stream": False,
        "messages": [{"role": "user", "content": "Suggest a name for a hiking club."}],
    },
    timeout=(10, 120),
)
data = resp.json()
if not resp.ok:
    raise SystemExit(f"{resp.status_code} {data.get('error')}")
print(data["choices"][0]["message"]["content"])
print(data["usage"])

Run it

python hello.py
⚠️

With the openai library, always pass stream=False on non-streaming calls. The API streams when stream is left out, and the library leaves it out unless you set it, so a missing stream=False produces a reply the library cannot parse.

A conversation with instructions

from openai import OpenAI
import os
 
client = OpenAI(base_url="https://app.syntic.ai/v1", api_key=os.environ["SYNTIC_API_KEY"])
messages = [{"role": "system", "content": "You are a trail guide for the Lake District. Keep answers under 80 words."}]
 
while True:
    question = input("you> ").strip()
    if not question:
        break
    messages.append({"role": "user", "content": question})
    r = client.chat.completions.create(model="Amara S1.2", stream=False, max_tokens=300, messages=messages)
    answer = r.choices[0].message.content or ""
    messages.append({"role": "assistant", "content": answer})
    print("guide>", answer)

The API keeps no state, so each request sends the whole messages list.

Stream the reply

stream = client.chat.completions.create(
    model="Amara Quick",
    stream=True,
    messages=[{"role": "user", "content": "Describe a mountain sunrise in three sentences."}],
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()

Call a function

import json
 
tools = [{"type": "function", "function": {
    "name": "trail_conditions",
    "description": "Current conditions for a named trail: open or closed, and any warnings.",
    "parameters": {"type": "object", "properties": {"trail": {"type": "string"}}, "required": ["trail"]},
}}]
 
def trail_conditions(trail):
    return {"trail": trail, "status": "open", "warnings": ["ice above 600 m"]}
 
messages = [{"role": "user", "content": "Is the Helvellyn Striding Edge route OK today?"}]
r = client.chat.completions.create(model="Amara S1.2", stream=False, tools=tools, messages=messages)
msg = r.choices[0].message
if msg.tool_calls:
    messages.append(msg.model_dump(exclude_none=True))
    for call in msg.tool_calls:
        result = trail_conditions(**json.loads(call.function.arguments))
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
    r = client.chat.completions.create(model="Amara S1.2", stream=False, tools=tools, messages=messages)
print(r.choices[0].message.content)

For a loop that keeps going until the model is done, see Tool loop in Python.

Handle errors

import openai
 
try:
    client.chat.completions.create(model="Amara S1.2", stream=False, messages=[])
except openai.APIStatusError as err:
    body = err.response.json()
    e = body["error"]
    print(err.status_code, e["type"], e["code"], e["request_id"])
    # 400 invalid_request_error messages_required req_...

The library retries 429 and 5xx responses twice by default; raise it with OpenAI(..., max_retries=5). For 402 quota_exhausted, see Handle 402.

Read Syntic headers

raw = client.chat.completions.with_raw_response.create(
    model="Amara S1.2", stream=False, messages=[{"role": "user", "content": "Hi"}],
)
print(raw.headers.get("x-syntic-billing-source"), raw.headers.get("x-syntic-model-downgraded"))
completion = raw.parse()

Next steps

Was this page helpful?