This comprehensive tutorial describes how to use the Sunbird AI API and includes code samples in Python.
- English (eng)
- Acholi (ach)
- Ateso (teo)
- Luganda (lug)
- Lugbara (lgg)
- Runyankole (nyn)
- Swahili (swa)
- Plus 20 more Ugandan languages
Coverage differs per endpoint: speech-to-text spans 51 African languages,
chat/translation spans 32 (sunflower-14b) or 67 (sunflower-9b), and
text-to-speech spans 20. See the matrix below.
The matrix below summarises which languages each task endpoint currently
serves. Code is the canonical ISO/SALT code the API accepts (full language
names also work where an endpoint takes a language or voice).
/tasks/audio/speech— text-to-speech (orpheus-3b-tts voice catalog)./tasks/audio/transcriptions— speech-to-text. Now covers 51 African languages via Sunbird's faster-whisper ASR model — see Part 3./tasks/chat/completions— Sunflower chat. The/tasks/chat/completionscolumn below reflects the defaultsunflower-14bmodel (English + 31 Ugandan/regional languages), which/tasks/translateshares. The alternativesunflower-9bmodel covers a broader set of 67 African languages — see Part 6 for the per-model breakdown.
| Language name | Code | /tasks/audio/speech |
/tasks/audio/transcriptions |
/tasks/chat/completions |
|---|---|---|---|---|
| Acholi | ach |
✅ | ✅ | ✅ |
| Afrikaans | afr |
✅ | ✅ | ❌ |
| Akan | aka |
❌ | ✅ | ❌ |
| Alur | alz |
❌ | ❌ | ✅ |
| Amharic | amh |
❌ | ✅ | ❌ |
| Aringa | luc |
❌ | ❌ | ✅ |
| Ateso | teo |
✅ | ✅ | ✅ |
| Bambara | bam |
❌ | ✅ | ❌ |
| Bari | bfa |
❌ | ❌ | ✅ |
| Bemba | bem |
❌ | ✅ | ❌ |
| Berber | ber |
❌ | ✅ | ❌ |
| Chichewa | nya |
❌ | ✅ | ❌ |
| Dagaare | dga |
❌ | ✅ | ❌ |
| Dagbani | dag |
❌ | ✅ | ❌ |
| English | eng |
✅ | ✅ | ✅ |
| Ewe | ewe |
✅ | ✅ | ❌ |
| French | fra |
❌ | ✅ | ❌ |
| Fulah / Fulani | ful |
✅ | ✅ | ❌ |
| Hausa | hau |
✅ | ✅ | ❌ |
| Igbo | ibo |
✅ | ✅ | ❌ |
| Ikposo | kpo |
❌ | ✅ | ❌ |
| Jopadhola | adh |
❌ | ❌ | ✅ |
| Kabyle | kab |
❌ | ✅ | ❌ |
| Kakwa | keo |
❌ | ❌ | ✅ |
| Kalenjin | kln |
❌ | ✅ | ❌ |
| Kanuri | kau |
❌ | ✅ | ❌ |
| Karamojong | kdj |
❌ | ❌ | ✅ |
| Kikuyu | kik |
✅ | ✅ | ❌ |
| Kinyarwanda | kin |
✅ | ✅ | ✅ |
| Kumam | kdi |
❌ | ❌ | ✅ |
| Kupsabiny | kpz |
❌ | ❌ | ✅ |
| Kwamba | rwm |
❌ | ✅ | ✅ |
| Lango | laj |
❌ | ❌ | ✅ |
| Lendu | led |
❌ | ✅ | ❌ |
| Lingala | lin |
✅ | ✅ | ❌ |
| Lubwisi | tlj |
❌ | ❌ | ✅ |
| Lugbara | lgg |
✅ † | ✅ | ✅ |
| Lugungu | rub |
❌ | ❌ | ✅ |
| Lugwere | gwr |
❌ | ❌ | ✅ |
| Luganda | lug |
✅ | ✅ | ✅ |
| Luhya | luy |
❌ | ✅ | ❌ |
| Lumasaba | myx |
❌ | ✅ | ✅ |
| Lunyole | nuj |
❌ | ❌ | ✅ |
| Luo (Dholuo) | luo |
✅ | ✅ | ❌ |
| Lusoga | xog |
❌ | ✅ | ✅ |
| Ma'di | mhi |
❌ | ❌ | ✅ |
| Malagasy | mlg |
❌ | ✅ | ❌ |
| Ndebele | nbl |
❌ | ✅ | ❌ |
| Nigerian Pidgin | pcm |
❌ | ✅ | ❌ |
| Oromo | orm |
❌ | ✅ | ❌ |
| Pokot | pok |
❌ | ❌ | ✅ |
| Rukiga | cgg |
❌ | ✅ | ✅ |
| Rukonjo | koo |
❌ | ✅ | ✅ |
| Runyankole | nyn |
✅ | ✅ | ✅ |
| Runyoro | nyo |
❌ | ❌ | ✅ |
| Ruruuli | ruc |
❌ | ✅ | ✅ |
| Rutooro | ttj |
❌ | ✅ | ✅ |
| Samia | lsm |
❌ | ❌ | ✅ |
| Sesotho / Sotho | sot |
✅ † | ✅ | ❌ |
| Setswana / Tswana | tsn |
✅ † | ✅ | ❌ |
| Shona | sna |
❌ | ✅ | ❌ |
| Somali | som |
❌ | ✅ | ❌ |
| Swahili | swa |
✅ | ✅ | ✅ |
| Thur | lth |
❌ | ✅ | ❌ |
| Wolof | wol |
❌ | ✅ | ❌ |
| Xhosa | xho |
✅ | ✅ | ❌ |
| Yoruba | yor |
✅ | ✅ | ❌ |
| Zulu | zul |
❌ | ✅ | ❌ |
† Lugbara, Sesotho, and Setswana are in the orpheus-3b-tts training mix but currently expose no individual voice IDs in this checkpoint, so practical synthesis depends on a future voice release.
- If you don't already have an account, create one at https://api.sunbird.ai/register
- Go to the tokens page to get your access token / api key
import requests
url = "https://api.sunbird.ai/auth/register"
data = {
"username": "your_username",
"email": "your_email@example.com",
"password": "your_secure_password"
}
response = requests.post(url, json=data)
print(response.json())import requests
url = "https://api.sunbird.ai/auth/token"
data = {
"username": "your_username",
"password": "your_password"
}
response = requests.post(url, data=data)
token_data = response.json()
access_token = token_data["access_token"]
print(f"Your token: {access_token}")Translate text between 32 Ugandan and East African languages using the
Sunflower model. Languages are accepted as ISO 639-3 codes (lug) or full
names (Luganda), case-insensitively, and translation works between any
pair of supported languages. source_language is optional — when omitted,
Sunflower infers it from the text.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
url = "https://api.sunbird.ai/tasks/translate"
access_token = os.getenv("AUTH_TOKEN")
headers = {
"accept": "application/json",
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
# Example: Translate from Luganda to English
data = {
"source_language": "lug",
"target_language": "eng",
"text": "Ekibiina ekiddukanya omuzannyo gw'emisinde mu ggwanga ekya Uganda Athletics Federation kivuddeyo nekitegeeza nga lawundi esooka eyemisinde egisunsulamu abaddusi abanakiika mu mpaka ezenjawulo ebweru w'eggwanga egya National Athletics Trials nga bwegisaziddwamu.",
}
response = requests.post(url, headers=headers, json=data)
print(response.json())source_language is optional, and full language names work too:
data = {
"target_language": "Luganda",
"text": "How are you?",
}
response = requests.post(url, headers=headers, json=data)
print(response.json())Supported languages (ISO code → name):
language_codes = {
"ach": "Acholi",
"adh": "Jopadhola",
"alz": "Alur",
"bfa": "Bari",
"cgg": "Rukiga",
"eng": "English",
"gwr": "Lugwere",
"kdi": "Kumam",
"kdj": "Karamojong",
"keo": "Kakwa",
"kin": "Kinyarwanda",
"koo": "Rukonjo",
"kpz": "Kupsabiny",
"laj": "Lango",
"lgg": "Lugbara",
"lsm": "Samia",
"luc": "Aringa",
"lug": "Luganda",
"mhi": "Ma'di",
"myx": "Lumasaba",
"nuj": "Lunyole",
"nyn": "Runyankole",
"nyo": "Runyoro",
"pok": "Pokot",
"rub": "Lugungu",
"ruc": "Ruruuli",
"rwm": "Kwamba",
"swa": "Swahili",
"teo": "Ateso",
"tlj": "Lubwisi",
"ttj": "Rutooro",
"xog": "Lusoga",
}The response shape is unchanged from the previous NLLB-backed endpoint:
{
"id": "trans-1a2b3c...",
"status": "COMPLETED",
"output": {
"translated_text": "Oli otya?",
"source_language": "lug",
"target_language": "eng"
}
}Convert speech audio to text. The unified POST /tasks/audio/transcriptions
endpoint is powered by Sunbird's faster-whisper ASR model
(Sunbird/faster-whisper-51-african-languages,
a Whisper large-v3 fine-tune)
covering 51 African languages. Supports MP3, WAV, M4A, OGG, and more.
Migrating from the legacy STT routes?
/tasks/modal/stt,/tasks/stt,/tasks/stt_from_gcs, and/tasks/org/sttare deprecated (they still work but returnDeprecation/Sunsetheaders). Switch to/tasks/audio/transcriptions.
Breaking change. This endpoint previously accepted
platform,adapter,whisper,recognise_speakers,org, andgcs_blob_name. Those parameters have been removed and are now rejected with422. Speaker diarization and the organization workflow are still available on the deprecated/tasks/org/sttand/tasks/sttroutes.
audio and language are both required. The model reuses Whisper's
language-token slots for African languages, which makes automatic language
detection unreliable — always pass a language explicitly.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
url = "https://api.sunbird.ai/tasks/audio/transcriptions"
access_token = os.getenv("AUTH_TOKEN")
headers = {
"accept": "application/json",
"Authorization": f"Bearer {access_token}",
}
audio_file_path = "/path/to/audio_file.wav"
files = {
"audio": ("recording.wav", open(audio_file_path, "rb"), "audio/wav"),
}
data = {
"language": "lug", # required: ISO 639-3 code, one of the 51 below
}
response = requests.post(url, headers=headers, files=files, data=data)
result = response.json()
print(f"Transcription: {result['audio_transcription']}")Example response:
{
"audio_transcription": "Ekibiina ekiddukanya ...",
"language": "lug",
"audio_url": "gs://.../audio.wav",
"audio_transcription_id": 123,
"duration_seconds": 12.4,
"segments": null,
"diarization_output": {},
"formatted_diarization_output": "",
"was_audio_trimmed": false,
"original_duration_minutes": null
}Set timestamps=true to also receive per-segment start/end times in segments.
It is false by default, in which case segments comes back as null.
data = {
"language": "ach",
"timestamps": True,
}
response = requests.post(url, headers=headers, files=files, data=data)
result = response.json()
print(result["audio_transcription"])
for segment in result["segments"]:
print(f"[{segment['start']:7.2f} -> {segment['end']:7.2f}] {segment['text']}"){
"audio_transcription": "Gyebaleko ssebo",
"language": "ach",
"duration_seconds": 2.25,
"segments": [
{"start": 0.0, "end": 1.0, "text": "Gyebaleko"},
{"start": 1.0, "end": 2.25, "text": "ssebo"}
]
}Note: For files larger than 100MB, only the first 10 minutes will be transcribed.
Pass the ISO 639-3 code as language. All ten languages served by the previous
version of this endpoint are still supported.
| Code | Language | Code | Language | Code | Language |
|---|---|---|---|---|---|
ach |
Acholi | kab |
Kabyle | nyn |
Runyankole |
afr |
Afrikaans | kau |
Kanuri | orm |
Oromo |
aka |
Akan | kik |
Kikuyu | pcm |
Nigerian Pidgin |
amh |
Amharic | kin |
Kinyarwanda | ruc |
Ruruuli |
bam |
Bambara | kln |
Kalenjin | rwm |
Kwamba |
bem |
Bemba | koo |
Rukonjo | sna |
Shona |
ber |
Berber | kpo |
Ikposo | som |
Somali |
cgg |
Rukiga | led |
Lendu | sot |
Sotho |
dag |
Dagbani | lgg |
Lugbara | swa |
Swahili |
dga |
Dagaare | lin |
Lingala | teo |
Ateso |
eng |
English | lth |
Thur | tsn |
Tswana |
ewe |
Ewe | lug |
Luganda | ttj |
Rutooro |
fra |
French | luo |
Luo | wol |
Wolof |
ful |
Fulani | luy |
Luhya | xho |
Xhosa |
hau |
Hausa | mlg |
Malagasy | xog |
Lusoga |
ibo |
Igbo | myx |
Lumasaba | yor |
Yoruba |
nbl |
Ndebele | zul |
Zulu | ||
nya |
Chichewa |
As a Python dictionary:
SALT_LANGUAGE_IDS_WHISPER = {
'ach': "Acholi", 'kab': "Kabyle", 'nyn': "Runyankole",
'afr': "Afrikaans", 'kau': "Kanuri", 'orm': "Oromo",
'aka': "Akan", 'kik': "Kikuyu", 'pcm': "Nigerian Pidgin",
'amh': "Amharic", 'kin': "Kinyarwanda", 'ruc': "Ruruuli",
'bam': "Bambara", 'kln': "Kalenjin", 'rwm': "Kwamba",
'bem': "Bemba", 'koo': "Rukonjo", 'sna': "Shona",
'ber': "Berber", 'kpo': "Ikposo", 'som': "Somali",
'cgg': "Rukiga", 'led': "Lendu", 'sot': "Sotho",
'dag': "Dagbani", 'lgg': "Lugbara", 'swa': "Swahili",
'dga': "Dagaare", 'lin': "Lingala", 'teo': "Ateso",
'eng': "English", 'lth': "Thur", 'tsn': "Tswana",
'ewe': "Ewe", 'lug': "Luganda", 'ttj': "Rutooro",
'fra': "French", 'luo': "Luo", 'wol': "Wolof",
'ful': "Fulani", 'luy': "Luhya", 'xho': "Xhosa",
'hau': "Hausa", 'mlg': "Malagasy", 'xog': "Lusoga",
'ibo': "Igbo", 'myx': "Lumasaba", 'yor': "Yoruba",
'nbl': "Ndebele", 'zul': "Zulu",
'nya': "Chichewa",
}Per-language accuracy scales with the amount of training data available for each language; see the model card for per-language WER/CER figures.
Automatically detect the language of text input. Useful for routing text to appropriate translation or processing pipelines.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
url = "https://api.sunbird.ai/tasks/language_id"
access_token = os.getenv("AUTH_TOKEN")
headers = {
"accept": "application/json",
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
text = "Oli otya? Webale nnyo ku kujja wano."
data = {"text": text}
response = requests.post(url, headers=headers, json=data)
result = response.json()
print(f"Detected language: {result}")Supported Languages: Acholi, Ateso, English, Luganda, Lugbara, Runyankole
Synthesize speech from text. The POST /tasks/audio/speech endpoint is served
by the RunPod Orpheus-3B deployment (orpheus-3b-tts) — multilingual,
multi-speaker; voices are catalog tags (e.g. salt_lug_0001). List them with
GET /tasks/voice/speakers.
Breaking change. This endpoint previously accepted
model,platform, andmax_new_audio_tokens, and offered aspark-ttsmodel. RunPod Orpheus is now the only TTS backend: those parameters are rejected, and the legacy per-provider routes (/tasks/modal/tts,/tasks/runpod/tts,/tasks/modal/orpheus/*,/tasks/tts, the/stream*and speaker-listing routes, andrefresh-url) were removed. Use the endpoints below.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
url = "https://api.sunbird.ai/tasks/audio/speech"
access_token = os.getenv("AUTH_TOKEN")
headers = {
"accept": "application/json",
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
payload = {
"text": "I am a nurse who takes care of many people.",
"voice": "salt_lug_0001", # catalog tag; see GET /tasks/voice/speakers
"language": "eng", # ISO code or full name; picks a voice if none given
"response_mode": "url", # "url" (default), "stream", or "both"
}
response = requests.post(url, headers=headers, json=payload)
print(response.status_code)
print(response.json())Speaker IDs encode both the source corpus (salt_*, waxal_*, slr32_*,
slr129_*, bateesa_*) and the language. Languages marked with an em dash
in the Speaker IDs column are present in the model's training mix but do
not currently expose individual voice IDs in this checkpoint.
| Config | Language | ISO 639-1 | Region | Speaker IDs |
|---|---|---|---|---|
ach |
Acholi | — | Uganda, South Sudan | salt_ach_0001waxal_ach_0001waxal_ach_0005waxal_ach_0006waxal_ach_0008 |
afr |
Afrikaans | af | South Africa, Namibia | slr32_afr_0009 |
eng |
English | en | (control language) | salt_eng_0001salt_eng_0002salt_eng_0003 |
ewe |
Ewe | ee | Ghana, Togo | slr129_ewe_0001 |
ful |
Fulah | ff | West Africa (Sahel) | waxal_ful_0003waxal_ful_0004waxal_ful_0006 |
hau |
Hausa | ha | Nigeria, Niger, Chad | waxal_hau_0004waxal_hau_0006waxal_hau_0007waxal_hau_0008 |
ibo |
Igbo | ig | Nigeria | waxal_ibo_0003waxal_ibo_0005waxal_ibo_0008 |
kik |
Kikuyu | ki | Kenya | waxal_kik_0003waxal_kik_0004 |
kin |
Kinyarwanda | rw | Rwanda | bateesa_kin_0001 |
lgg |
Lugbara | — | Uganda, DRC | — |
lin |
Lingala | ln | DRC, Republic of Congo | slr129_lin_0001 |
lug |
Luganda | lg | Uganda | salt_lug_0001waxal_lug_0002waxal_lug_0003waxal_lug_0004waxal_lug_0005waxal_lug_0006waxal_lug_0007waxal_lug_0008 |
luo |
Luo (Dholuo) | — | Kenya, Tanzania | waxal_luo_0001waxal_luo_0002waxal_luo_0003waxal_luo_0004 |
nyn |
Runyankole | — | Uganda | salt_nyn_0001waxal_nyn_0003waxal_nyn_0004waxal_nyn_0007waxal_nyn_0008 |
sot |
Sesotho | st | Lesotho, South Africa | — |
swa |
Swahili | sw | East Africa | waxal_swa_0006waxal_swa_0007 |
teo |
Ateso | — | Uganda, Kenya | salt_teo_0001 |
tsn |
Setswana | tn | Botswana, South Africa | — |
xho |
Xhosa | xh | South Africa | slr32_xho_0012 |
yor |
Yoruba | yo | Nigeria, Benin | waxal_yor_0002waxal_yor_0006waxal_yor_0008 |
Per-language quality scales with the amount of training data Sunbird collected for that language; some configs have many more speaker hours than others. Audition the voices for each language before relying on a particular speaker — use the discovery snippet under Listing voices below.
response_mode controls how the audio is returned:
url— synthesize, upload to GCP, return a signed URL (valid ~30 minutes) — defaultstream— stream the raw audio bytes straight back (constant client memory; no GCS upload)both— stream the raw audio bytes and return the signed URL in theX-Audio-Urlresponse header
Note: RunPod synthesizes the full waveform before sending the first byte, so
stream bounds client memory but does not reduce time-to-first-audio.
auth = {"Authorization": f"Bearer {access_token}"}
# Orpheus voices grouped by language (default)
print(requests.get("https://api.sunbird.ai/tasks/voice/speakers", headers=auth).json())
# Voices for one language
print(requests.get(
"https://api.sunbird.ai/tasks/voice/speakers",
headers=auth,
params={"language": "lug"},
).json())Synthesize up to 16 items in a single request (RunPod /tts/batch cap):
url = "https://api.sunbird.ai/tasks/audio/speech/batch"
payload = {
"items": [
{"text": "Good morning.", "voice": "salt_lug_0001"},
{"text": "How are you?", "voice": "salt_eng_0001"},
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())Signed URLs expire after ~30 minutes. Re-sign a stored object with GET /tasks/audio/speech/url:
print(requests.get(
"https://api.sunbird.ai/tasks/audio/speech/url",
headers={"Authorization": f"Bearer {access_token}"},
params={"gcs_object": "orpheus_tts/2026-06-03/abc.wav"},
).json())Example response (POST /tasks/audio/speech):
{
"audio_url": "https://storage.googleapis.com/.../tts_audio/....wav?...",
"model": "orpheus-3b-tts",
"platform": "runpod",
"voice": "salt_lug_0001",
"audio_url_expires_at": "2026-06-03T22:59:36.954061Z",
"language": "lug",
"sample_rate": 24000,
"duration_seconds": 4.0,
"gcs_object": "orpheus_tts/2026-06-03/....wav",
"request_id": "0f1e2d3c4b5a...",
"timings_ms": {"inference_ms": 1820.5, "upload_ms": 234.1, "total_ms": 2095.6}
}The Sunflower models provide conversational AI for African languages through an OpenAI-compatible endpoint: POST /tasks/chat/completions. The request and response formats mirror the OpenAI Chat Completions API, so you can move between the OpenAI API and the Sunbird API by changing only the base URL and API key.
Deprecated:
POST /tasks/sunflower_inferenceandPOST /tasks/sunflower_simpleare deprecated and will be removed in a future release. UsePOST /tasks/chat/completionsinstead — a single instruction is just a request with one user message.
Select a model with the model field. Two models are available:
model |
Coverage | Best for |
|---|---|---|
sunflower-14b (default) |
English + 31 Ugandan / regional languages (32 total) | High-accuracy translation, factual Q&A, summarization, and explanation across Ugandan languages |
sunflower-9b |
67 African languages (good / moderate / basic tiers) | Broad pan-African translation, instruction-following, and multi-turn chat |
sunflower-14b is the default when model is omitted. The old identifier
Sunbird/Sunflower-14B is no longer accepted — sending it returns a 400
error asking you to use sunflower-14b instead.
sunflower-14b — supported languages (English + 31 Ugandan/regional):
Acholi (ach), Alur (alz), Aringa (luc), Ateso (teo), Bari (bfa),
English (eng), Jopadhola (adh), Kakwa (keo), Karamojong (kdj),
Kinyarwanda (kin), Kumam (kdi), Kupsabiny (kpz), Kwamba (rwm),
Lango (laj), Lubwisi (tlj), Lugbara (lgg), Lugungu (rub),
Lugwere (gwr), Luganda (lug), Lumasaba (myx), Lunyole (nuj),
Ma'di (mhi), Pokot (pok), Rukiga (cgg), Rukonjo (koo),
Runyankole (nyn), Runyoro (nyo), Ruruuli (ruc), Rutooro (ttj),
Samia (lsm), Swahili (swa), Lusoga (xog).
sunflower-9b — supported languages (67 African languages, grouped by the
model's understanding tier):
- Good: Afrikaans, Zulu, Nigerian Pidgin, Chichewa, Swahili, Xhosa, Somali, Lingala, Sotho, Igbo, Malagasy, Kinyarwanda, Hausa, Tswana, Yoruba, Luganda, Shona, Ndebele, Amharic, Kirundi, Runyoro, Luo, Ewe, Runyankole.
- Moderate: Kikuyu, Lusoga, Acholi, Rukiga, Rutooro, Oromo, Bemba, Ruruuli, Akan, Lango, Lugwere, Lugbara, Kumam, Lugungu, Lumasaba, Ateso, Lunyole.
- Basic: Kabyle, Rukonjo, Bambara, Lubwisi, Wolof, Samia, Jopadhola, Alur, Bari, Kakwa, Dagbani, Karamojong, Berber, Fulani, Kwamba, Ma'di, Aringa, Luhya, Kalenjin, Kupsabiny, Lendu, Dagaare, Ik, Pokot, Kanuri, Dinka.
Per-language quality scales with the amount of training data available for each language. Full evaluations and language codes are on the model pages: sunflower-14b and sunflower-qwen3.5-9b.
import requests
url = "https://api.sunbird.ai/tasks/chat/completions"
headers = {
"accept": "application/json",
"Authorization": "Bearer <your-access-token>",
"Content-Type": "application/json",
}
payload = {
"model": "sunflower-14b",
"messages": [
{
"role": "user",
"content": "Good morning, what is the weather today?",
}
],
"temperature": 0.3,
}
response = requests.post(url, headers=headers, json=payload)
print(response.status_code)
print(response.json())Example Response:
{
"id": "chatcmpl-8f14e45fceea167a5a36dedd4bea2543",
"object": "chat.completion",
"created": 1718000000,
"model": "sunflower-14b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "I'm glad you're up! While I can't provide real-time weather updates, I can help you understand weather forecasts or explain common weather patterns in Uganda."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 22,
"completion_tokens": 47,
"total_tokens": 69
}
}Because the endpoint is OpenAI-compatible, the official OpenAI Python SDK works out of the box:
from openai import OpenAI
client = OpenAI(
api_key="<your-access-token>",
base_url="https://api.sunbird.ai/tasks",
)
completion = client.chat.completions.create(
model="sunflower-14b",
messages=[
{
"role": "user",
"content": "translate from english to luganda: i am very hungry they should serve food in time",
}
],
temperature=0.1,
)
print(completion.choices[0].message.content)
# Ndi muyala nnyo, emmere erina okugabibwa mu budde.Maintain context by sending the running message history. You can also set a custom system message (when omitted, a default Sunflower system message is applied):
payload = {
"model": "sunflower-14b",
"messages": [
{"role": "system", "content": "You are a helpful multilingual assistant."},
{"role": "user", "content": "Translate 'hello' to Luganda."},
{"role": "assistant", "content": "'Hello' is 'Gyebaleko'."},
{"role": "user", "content": "And to Acholi?"},
],
}Set "stream": true to receive Server-Sent Events in OpenAI chat.completion.chunk format, terminated by data: [DONE]. With the OpenAI SDK:
stream = client.chat.completions.create(
model="sunflower-14b",
messages=[{"role": "user", "content": "Tell me about Uganda."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)Supported request parameters: model (sunflower-14b or sunflower-9b; the old Sunbird/Sunflower-14B identifier is no longer accepted), messages, temperature (0.0-2.0, default 0.3), max_tokens, top_p, stop, and stream.
Generate secure signed URLs for direct client uploads to GCP Storage. Useful for uploading audio files before transcription.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
# Step 1: Generate upload URL
url = "https://api.sunbird.ai/tasks/generate-upload-url"
access_token = os.getenv("AUTH_TOKEN")
headers = {
"accept": "application/json",
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
data = {
"file_name": "recording.wav",
"content_type": "audio/wav"
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
upload_url = result["upload_url"]
file_id = result["file_id"]
print(f"Upload URL: {upload_url}")
print(f"File ID: {file_id}")
print(f"Expires at: {result['expires_at']}")
# Step 2: Upload file directly to GCS
with open("/path/to/your/recording.wav", "rb") as f:
upload_response = requests.put(
upload_url,
data=f,
headers={"Content-Type": "audio/wav"}
)
if upload_response.status_code == 200:
print("File uploaded successfully!")Features:
- Temporary signed URLs (valid for 30 minutes)
- Direct upload to Google Cloud Storage
- Path traversal protection
- Support for multiple content types
- API Documentation: https://api.sunbird.ai/docs
- OpenAPI Specification: https://api.sunbird.ai/openapi.json
- Usage Guide: https://salt.sunbird.ai/API/
API endpoints are rate-limited to ensure fair usage. If you need higher rate limits for production use, please contact the Sunbird AI team.
Don't hesitate to leave us any feedback or questions by opening an issue in this repo.