Skip to content

Python SDK

The Python SDK provides both synchronous and asynchronous clients with Pydantic models for every request and response.

Terminal window
pip install surveycoder

Requires Python 3.9+. The async client requires httpx[async], included by default.

from surveycoder import SurveyCoder
import os
sc = SurveyCoder(api_key=os.environ["SCP_API_KEY"])
result = sc.code(
question="Which brand of laundry detergent do you prefer and why?",
responses=[
"Tide because it removes stains better",
"Ariel - bigger pack, lasts longer",
"Persil because my mom always used it",
],
)
for entry in result.codebook:
print(entry.code, entry.category, entry.count)
from surveycoder import AsyncSurveyCoder
import asyncio
import os
async def main():
async with AsyncSurveyCoder(api_key=os.environ["SCP_API_KEY"]) as sc:
result = await sc.code(
question="...",
responses=[...],
)
print(result.codebook)
asyncio.run(main())

AsyncSurveyCoder is a drop-in async equivalent — same method names, same return models, await-able.

SurveyCoder(
api_key: str, # required
base_url: str = "https://api.surveycoder.io",
timeout: float = 60.0,
max_retries: int = 2, # on 5xx and 429
default_idempotency: str = "auto", # "auto" | "off"
)
Sub-clientEndpoint group
sc.code(...)POST /v1/code
sc.projectsProjects and questions CRUD
sc.jobsAsync job status and waiting
sc.codebooksGenerate, import, clone, estimate
sc.qualityCross-coder consistency
sc.refinementsSuggestions: list, apply, resolve
sc.usageBalance and history
sc.webhooksRegister and rotate webhooks
job = sc.code(question="...", responses=[...]) # 50+ responses
result = sc.jobs.wait(job.job_id, poll_interval=3.0, timeout=600.0)
print(result.codebook)

sc.jobs.wait raises SurveyCoderError if the job ends in failed. It honours Retry-After from the server when present.

from surveycoder import SurveyCoder, SurveyCoderError
try:
sc.code(...)
except SurveyCoderError as e:
print(e.code) # 'INSUFFICIENT_CREDITS'
print(e.message)
print(e.request_id) # 'req_01HXJZK4...'
print(e.doc_url)
print(e.status)

Every code is documented in the error reference.

All request and response payloads are Pydantic v2 models, importable from the package root:

from surveycoder import (
CodeRequest,
CodeResponse,
Codebook,
CodedRow,
)
req = CodeRequest(question="...", responses=[...])
sc.code(**req.model_dump())

The SDK ships a helper that handles raw-body signing and timing-safe comparison:

from surveycoder import verify_webhook
from fastapi import FastAPI, Request, HTTPException
import os
app = FastAPI()
SECRET = os.environ["SCP_WEBHOOK_SECRET"]
@app.post("/hooks/surveycoder")
async def hook(request: Request):
body = await request.body()
if not verify_webhook(body, request.headers.get("x-sc-signature"), SECRET):
raise HTTPException(status_code=401)
# ... handle event