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 openaiSend 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
- Stream in Python: async and error handling.
- Count cost from usage
- Guides
Was this page helpful?