Documentation Index Fetch the complete documentation index at: https://docs.sully.ai/llms.txt
Use this file to discover all available pages before exploring further.
The official Python SDK provides a convenient way to interact with the Sully.ai API from your Python applications. It includes type hints, Pydantic models for response validation, and supports both synchronous and asynchronous usage.
Installation
Install the SDK using pip:
Quick Setup
Environment Variables
Set your API credentials as environment variables:
export SULLY_API_KEY = "your-api-key"
export SULLY_ACCOUNT_ID = "your-account-id"
Initialize the Client
from sullyai import SullyAI
# Client automatically reads from environment variables
client = SullyAI()
# Or pass credentials explicitly
client = SullyAI(
api_key = "your-api-key" ,
account_id = "your-account-id"
)
Sync vs Async
The SDK provides both synchronous and asynchronous clients to fit your application’s needs.
Synchronous Client
Use SullyAI for standard synchronous operations:
from sullyai import SullyAI
client = SullyAI()
# Make synchronous API calls
transcription = client.audio.transcriptions.create(
audio = open ( "recording.mp3" , "rb" )
)
Asynchronous Client
Use AsyncSullyAI for async applications with asyncio:
import asyncio
from sullyai import AsyncSullyAI
async def main ():
client = AsyncSullyAI()
# Make async API calls
transcription = await client.audio.transcriptions.create(
audio = open ( "recording.mp3" , "rb" )
)
print (transcription.transcription_id)
asyncio.run(main())
Audio Transcriptions
Upload a File
Submit an audio file for transcription:
from sullyai import SullyAI
client = SullyAI()
# Upload an audio file
with open ( "patient-visit.mp3" , "rb" ) as audio_file:
transcription = client.audio.transcriptions.create(
audio = audio_file
)
print ( f "Transcription ID: { transcription.transcription_id } " )
Poll for Completion
Audio transcription is an asynchronous process. Poll until it completes:
Synchronous polling:
import time
from sullyai import SullyAI
client = SullyAI()
# Upload the file
with open ( "patient-visit.mp3" , "rb" ) as audio_file:
transcription = client.audio.transcriptions.create( audio = audio_file)
# Poll until complete
while True :
result = client.audio.transcriptions.retrieve(transcription.transcription_id)
if result.status == "STATUS_SUCCEEDED" :
print ( f "Transcription: { result.payload.transcription } " )
break
elif result.status == "STATUS_ERROR" :
raise Exception ( "Transcription failed" )
time.sleep( 2 )
Asynchronous polling:
import asyncio
from sullyai import AsyncSullyAI
async def transcribe_audio ( file_path : str ) -> str :
client = AsyncSullyAI()
# Upload the file
with open (file_path, "rb" ) as audio_file:
transcription = await client.audio.transcriptions.create( audio = audio_file)
# Poll until complete
while True :
result = await client.audio.transcriptions.retrieve(transcription.transcription_id)
if result.status == "STATUS_SUCCEEDED" :
return result.payload.transcription
elif result.status == "STATUS_ERROR" :
raise Exception ( "Transcription failed" )
await asyncio.sleep( 2 )
# Run the async function
transcript = asyncio.run(transcribe_audio( "patient-visit.mp3" ))
Delete a Transcription
Remove a transcription when no longer needed:
client.audio.transcriptions.delete( transcription_id = "tr_abc123" )
Notes
Create a SOAP Note
Generate a SOAP note from a transcript:
from sullyai import SullyAI
client = SullyAI()
note = client.notes.create(
transcript = "Patient reports headache for 3 days. Pain is moderate, located in frontal region. No nausea or visual disturbances. Recommending OTC pain relief and follow-up if symptoms persist." ,
note_type = { "type" : "soap" }
)
print ( f "Note ID: { note.note_id } " )
Create with Context
Provide additional context to improve note quality:
from sullyai import SullyAI
client = SullyAI()
note = client.notes.create(
transcript = "Patient reports headache for 3 days..." ,
note_type = { "type" : "soap" },
date = "2024-01-15" ,
patient_info = {
"name" : "Jane Doe" ,
"date_of_birth" : "1985-03-22" ,
"gender" : "female"
},
medication_list = [
{ "name" : "Lisinopril" , "dosage" : "10mg" , "frequency" : "daily" },
{ "name" : "Metformin" , "dosage" : "500mg" , "frequency" : "twice daily" }
],
instructions = [
"Include vital signs in assessment" ,
"Note any medication interactions"
]
)
Retrieve a Note
Poll for the completed note:
import time
from sullyai import SullyAI
client = SullyAI()
# Create the note
note = client.notes.create(
transcript = "Patient visit transcript..." ,
note_type = { "type" : "soap" }
)
# Poll until complete
while True :
result = client.notes.retrieve(note.note_id)
if result.status == "STATUS_SUCCEEDED" :
print ( f "Markdown: { result.payload.markdown } " )
print ( f "JSON: { result.payload.json } " )
break
elif result.status == "STATUS_ERROR" :
raise Exception ( "Note generation failed" )
time.sleep( 2 )
Response Handling
Pydantic Models
All API responses are returned as Pydantic models with full type hints:
from sullyai import SullyAI
client = SullyAI()
transcription = client.audio.transcriptions.create(
audio = open ( "recording.mp3" , "rb" )
)
# Access typed attributes
print (transcription.transcription_id) # str
print (transcription.status) # str
Serialization Methods
Convert responses to different formats:
# Convert to JSON string
json_str = transcription.to_json()
# Convert to dictionary
data_dict = transcription.to_dict()
Distinguishing Null vs Missing Fields
Use model_fields_set to check which fields were explicitly returned:
result = client.notes.retrieve(note_id)
# Check if a field was in the response
if "payload" in result.model_fields_set:
# Field was present (even if None)
print (result.payload)
else :
# Field was not in the response
print ( "Payload not yet available" )
Error Handling
The SDK raises specific exceptions for different error conditions:
from sullyai import SullyAI
from sullyai.errors import (
APIError,
AuthenticationError,
RateLimitError,
BadRequestError,
NotFoundError,
APIConnectionError
)
client = SullyAI()
try :
note = client.notes.retrieve( "invalid-id" )
except AuthenticationError:
print ( "Invalid API key or account ID" )
except RateLimitError as e:
print ( f "Rate limited. Retry after: { e.retry_after } " )
except BadRequestError as e:
print ( f "Invalid request: { e.message } " )
except NotFoundError:
print ( "Resource not found" )
except APIConnectionError:
print ( "Failed to connect to Sully API" )
except APIError as e:
print ( f "API error: { e.status_code } - { e.message } " )
Exception Classes
Exception Description APIErrorBase class for all API errors AuthenticationErrorInvalid or missing API credentials RateLimitErrorToo many requests; includes retry_after BadRequestErrorInvalid request parameters NotFoundErrorRequested resource does not exist APIConnectionErrorNetwork connectivity issues
Automatic Retries
The SDK automatically retries failed requests with exponential backoff. By default, it attempts 2 retries for transient errors (network issues, 5xx errors).
from sullyai import SullyAI
# Use default retry behavior (2 attempts)
client = SullyAI()
# Customize max retries
client = SullyAI( max_retries = 5 )
# Disable retries
client = SullyAI( max_retries = 0 )
Timeouts
Configure timeouts to control how long the client waits for responses.
Global Timeout
Set a default timeout for all requests:
from sullyai import SullyAI
# Timeout in seconds
client = SullyAI( timeout = 30.0 )
Per-Request Timeout
Override the timeout for individual requests:
# This request has a longer timeout for large files
transcription = client.audio.transcriptions.create(
audio = large_audio_file,
timeout = 120.0
)
Next Steps
Audio Transcription Guide Learn about transcription options and best practices
Error Handling Guide Understand API events and error responses