Python SDK
Official Python SDK for FirstHandAPI. Supports both sync and async usage.
Installation
pip install firsthandRequires Python 3.9+.
Quick Start
from firsthand import FirstHandClient
client = FirstHandClient(api_key="fh_live_...")
# Post a job
job = client.create_job({
"type": "data_collection",
"description": "Take a clear photo of any coffee cup from above. Must show the full cup.",
"files_needed": 50,
"accepted_formats": ["image/jpeg", "image/png"],
"price_per_file_cents": 75,
})
# Get approved files
files = client.get_job_files(job["id"])Async Usage
from firsthand import AsyncFirstHandClient
async with AsyncFirstHandClient(api_key="fh_live_...") as client:
job = await client.create_job({
"type": "data_collection",
"description": "Take a clear photo of any coffee cup from above. Must show the full cup.",
"files_needed": 50,
"accepted_formats": ["image/jpeg", "image/png"],
"price_per_file_cents": 75,
})Configuration
client = FirstHandClient(
api_key="fh_live_...",
base_url="https://api.firsthandapi.com", # default
timeout=30.0, # 30s default
max_retries=3, # default
)Context Manager
with FirstHandClient(api_key="fh_live_...") as client:
job = client.create_job({...})
# Connection is closed automaticallyFeatures
- Sync and async —
FirstHandClient(sync) andAsyncFirstHandClient(async) - Auto-retry — Exponential backoff with jitter on 429 and 5xx errors
- Idempotency — Automatic UUID generation for all POST requests
- Webhook verification —
verify_webhook_signature()with timing-safe comparison
Methods
Jobs
create_job(body)— Post a content collection jobget_job(job_id)— Get job by IDlist_jobs(**params)— List jobs with filterscancel_job(job_id)— Cancel an open jobget_job_files(job_id, **params)— Get approved files for a jobget_job_submissions(job_id, **params)— List all submissions for a job
Submissions
list_submissions(**params)— List submissions across all jobsget_submission(submission_id)— Get submission by IDget_submission_summary(**params)— Aggregate submission metrics
API Keys
create_api_key(body)— Create a new API keylist_api_keys()— List API keysrotate_api_key(key_id)— Rotate with 24h overlaprevoke_api_key(key_id)— Revoke immediately
Webhooks
create_webhook_endpoint(body)— Create endpointlist_webhook_endpoints()— List endpointsget_webhook_endpoint(id)— Get endpointupdate_webhook_endpoint(id, body)— Update endpointdelete_webhook_endpoint(id)— Delete endpointrotate_webhook_secret(id)— Rotate signing secret
Webhook Events
list_webhook_events()— List eventsreplay_webhook_event(id)— Replay an event
Billing
get_credit_balance()— Current credit balancelist_transactions()— Transaction historypurchase_credits(body)— Initiate Stripe Checkout
Organization
get_settings()— Get org settingsupdate_settings(body)— Update org settings
Error Handling
from firsthand import FirstHandClient, FirstHandError
try:
job = client.create_job({"description": ""})
except FirstHandError as e:
print(e.status) # 400
print(e.type) # "validation_error"
print(e.request_id) # "req_01JQ..."
print(e.retryable) # FalseError Types
| Class | Description |
|---|---|
FirstHandError | Base error for API responses (non-2xx) |
FirstHandRateLimitError | 429 responses, includes retry_after |
FirstHandConnectionError | Network failures and timeouts |