SDKsPython

Python SDK

Official Python SDK for FirstHandAPI. Supports both sync and async usage.

Installation

pip install firsthand

Requires 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 automatically

Features

  • Sync and asyncFirstHandClient (sync) and AsyncFirstHandClient (async)
  • Auto-retry — Exponential backoff with jitter on 429 and 5xx errors
  • Idempotency — Automatic UUID generation for all POST requests
  • Webhook verificationverify_webhook_signature() with timing-safe comparison

Methods

Jobs

  • create_job(body) — Post a content collection job
  • get_job(job_id) — Get job by ID
  • list_jobs(**params) — List jobs with filters
  • cancel_job(job_id) — Cancel an open job
  • get_job_files(job_id, **params) — Get approved files for a job
  • get_job_submissions(job_id, **params) — List all submissions for a job

Submissions

  • list_submissions(**params) — List submissions across all jobs
  • get_submission(submission_id) — Get submission by ID
  • get_submission_summary(**params) — Aggregate submission metrics

API Keys

  • create_api_key(body) — Create a new API key
  • list_api_keys() — List API keys
  • rotate_api_key(key_id) — Rotate with 24h overlap
  • revoke_api_key(key_id) — Revoke immediately

Webhooks

  • create_webhook_endpoint(body) — Create endpoint
  • list_webhook_endpoints() — List endpoints
  • get_webhook_endpoint(id) — Get endpoint
  • update_webhook_endpoint(id, body) — Update endpoint
  • delete_webhook_endpoint(id) — Delete endpoint
  • rotate_webhook_secret(id) — Rotate signing secret

Webhook Events

  • list_webhook_events() — List events
  • replay_webhook_event(id) — Replay an event

Billing

  • get_credit_balance() — Current credit balance
  • list_transactions() — Transaction history
  • purchase_credits(body) — Initiate Stripe Checkout

Organization

  • get_settings() — Get org settings
  • update_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)   # False

Error Types

ClassDescription
FirstHandErrorBase error for API responses (non-2xx)
FirstHandRateLimitError429 responses, includes retry_after
FirstHandConnectionErrorNetwork failures and timeouts