Developers

Full API guide

Run the complete flow with projects, generations, progress checks, downloads, and webhooks. Get setup details, API behavior, responses, and workflows.

This guide expands on the quickstart and walks through most of the Diffio SDK surface. You will create projects, run generations, track progress, download results, list resources, and set up webhooks so your app can react to status changes. If you want the shortest path, start with the Quickstart.

Prerequisites

Before you start, have these ready.

  • A Diffio API key with read and write permissions.
  • A speech focused audio file, for example sample.wav.
  • Python 3.8 or later, or Node 18 or later.

Workflow overview

Projects hold your media. Each project can have one or more generations, and you can download each result.

Diffio restoration workflowCreate a project with a media file, run multiple generations with different models, then download the results.Create projectwith mediadiffio-4.5-flashdiffio-4.5-proGenerationsDownload results
A project contains your media. Create multiple generations under it, then download each restored file.
Prefer webhooks in production

Polling works for quick tests. For production, use webhooks so Diffio can notify you when a generation completes. See the Webhooks guide.

Using the Diffio SDK

Follow these steps in order, and add each block to the same file. You will create a project, run a generation, wait for completion, and download the restored audio.

  1. 1

    Create an API key

    Create an API key in the developer dashboard and store it as a secret. Pass the key on every request using the Authorization header (X-Api-Key and Xi-Api-Key also work).

    .env
    DIFFIO_API_KEY=diffio_live_...
    Tip

    Store your API key as a managed secret instead of committing it to source control.

  2. 2

    Install the SDK

    Install the Diffio Python or Node SDK, then load your API key from an environment file.

    Python
    pip install diffio python-dotenv
    SDK source code

    Browse the SDK repositories on GitHub: diffio-js and diffio-python .

    Upgrading an existing integration

    Diffio 4.5 Flash and Diffio 4.5 Pro are the only models. Requests that name any other model id are rejected with HTTP 410 and the code model_retired. Use diffio-4.5-flash or diffio-4.5-pro, and upgrade to SDK 0.2.0 or later, which uploads through the Diffio upload API. Earlier releases cannot create projects. Breaking changes in 0.2.0 for both SDKs:

    • Only diffio-4.5-flash (the default) and diffio-4.5-pro are accepted. diffio-2, diffio-2-flash, diffio-3.4, diffio-3.5, diffio-4.0-flash, and diffio-4.0-pro are removed and rejected before a request is sent. Generations created with older models are still listed and downloadable.
    • The project creation response drops uploadUrl, uploadMethod, and bucket, and adds upload (the upload session) and uploadCompletion.
    • The download response drops bucket.
    • Node: upload failures raise DiffioUploadError, and waitForGeneration waits up to 600 seconds by default instead of 60.
    Note

    Use Node 18 or later so fetch is available without extra packages.

  3. 3

    Configure the client

    Create a file named full_guide.py or full_guide.mjs. Then initialize the client and set global request options like timeouts and retries.

    Python
    from dotenv import load_dotenvfrom diffio import DiffioApiError, DiffioClient, RequestOptionsimport osload_dotenv()client = DiffioClient(    apiKey=os.getenv("DIFFIO_API_KEY"),    requestOptions=RequestOptions(        timeoutInSeconds=60,        maxRetries=2,    ),)
    Per call overrides

    Every SDK call accepts requestOptions so you can override timeouts, retries, headers, or the API key for a single request. Generation creation requires a nonblank idempotencyKey before automatic retries are allowed.

  4. 4

    Create a project

    Create a project for your media file. The SDK uploads the file as part of project creation. You can attach optional metadata through params.

    Python
    file_path = "sample.wav"project = client.create_project(    filePath=file_path,    fileFormat="wav",    params={        "source": "full-guide",        "notes": "First upload from docs",    },)
    How the upload works

    create_project and createProject upload the file to the Diffio upload API in 32 MiB parts (start, parts, complete) using only the session's upload token; your API key is never sent there. If a part fails, the SDK aborts the multipart upload. The SDK then calls /v1/complete_project_upload, and the returned project has upload (the session) and uploadCompletion (status and sizeBytes). Files larger than 2 GiB are refused by create_project with 413Client error, treated as fix required. and code UPLOAD_TOO_LARGE before any bytes are sent.

    Python sends one part at a time and retries a failed part on its own only when maxRetries is set. An error response from the upload API raises DiffioApiError; network errors raise the underlying httpx exception. Node uploads three parts at a time and tries each upload call up to four times on network errors, 408Client error, treated as fix required., 429Rate limit, treated as throttled., and 5xx answers, independent of maxRetries. It raises DiffioUploadError with uploadErrorCode, edgeErrorCode, and apiProjectId. Node leaves the upload token out of the returned upload; Python keeps it as upload.uploadToken.

    If the upload finished but the confirmation call failed, confirm it with client.complete_project_upload(apiProjectId=...) or client.projects.complete_upload in Python, or client.completeProjectUpload or client.projects.completeUpload in Node. The call is idempotent.

  5. 5

    Create a generation

    Start a generation with the model you want. Each Diffio 4.5 model uses its own fixed sampling profile, so sampling and params do not change the result; the SDKs still accept them, and the API stores them with the generation. The optional idempotencyKey makes retries safe; repeating a request with the same key returns the existing generation instead of creating and billing a duplicate. The response includes idempotentReplay set to true when it reused that generation.

    Python and Node SDKs automatically retry generation creation only with a nonblank idempotencyKey. Without a key, creation is attempted once, including on network errors, even when client or per-call maxRetries enables retries. Automatic retries preserve the same key and request body. Reuse both when manually retrying an uncertain response; other requests keep their configured retry policy.

    Python
    generation = client.generations.create(    apiProjectId=project.apiProjectId,    model="diffio-4.5-flash",    idempotencyKey="docs-guide-restore-1",)
  6. 6

    Wait for completion

    Poll until the overall status is complete. The wait helper returns the full progress object. Individual stages reaching 100% or complete do not end polling while required media artifacts or usage settlement are still pending. A terminal failed status causes the wait helper to raise an error. Both SDKs wait up to 600 seconds by default; pass timeout in Python or timeoutInSeconds in Node to change it.

    Progress also reports stage (the step the generation is in, such as queued, restoring, or uploading), stageProgress (percentages and byte counts while a processing worker runs it), and queue (position and a message while it waits for a worker). Each is None in Python or undefined in Node when the API does not report it.

    Python
    progress = client.generations.wait_for_complete(    generationId=generation.generationId,    apiProjectId=project.apiProjectId,    pollInterval=3.0,    timeout=900.0,    showProgress=True,)print("Final status:", progress.status)if progress.transcription is not None:    print("Transcription:", progress.transcription.status)
    Independent transcription

    Diffio 4.5 transcribes the recording before restoration starts, so a complete generation always has its transcript. Read progress.transcription.status for pending, available, or unavailable while a generation is running.

    This applies to waitForGeneration, wait_for_generation, generations.waitForComplete, and generations.wait_for_complete. Older responses omit transcription; SDKs expose that absence as undefined in Node or None in Python. Check for it before reading status.

    Media recovery

    Temporary preprocessing or video restoration failures can recover automatically. Keep polling the same generation during recovery. Retries are scheduled for up to approximately five minutes after the first temporary failure; this is not a limit on healthy processing time. If recovery fails, the generation reports failed and its pending usage reservation is released. Permanent input errors can fail immediately.

    Shortcut

    You can combine creation and polling with client.generations.create_and_wait in Python or client.generations.createAndWait in Node.

  7. 7

    Download results

    Save the restored audio, then request any available transcript JSON. downloadUrl is a signed Diffio media URL that needs no Authorization header and is valid for 6 hours, so request a new URL after it expires. The example attempts the transcript download once and reports pending or unavailable transcription separately.

    Python
    client.generations.download(    generationId=generation.generationId,    apiProjectId=project.apiProjectId,    downloadFilePath="restored.mp3",    downloadType="mp3",)try:    client.generations.download(        generationId=generation.generationId,        apiProjectId=project.apiProjectId,        downloadFilePath="transcript.json",        downloadType="transcript",    )except DiffioApiError as exc:    body = exc.responseBody if isinstance(exc.responseBody, dict) else {}    if exc.statusCode == 409 and body.get("code") == "TRANSCRIPT_PENDING":        print("Transcript pending; check progress and retry later.")    elif exc.statusCode == 404 and body.get("code") == "TRANSCRIPT_UNAVAILABLE":        print("Transcript unavailable; restored audio was saved.")    else:        raise
    Troubleshooting

    If you receive 401Unauthorized, treated as auth error. or 403Forbidden, treated as permission error., confirm the API key is valid and has read and write permissions. A 402Payment required, billing issue such as paymentFailed. API usage can be blocked until billing is updated. error indicates a billing issue: needsPaymentMethod when the $5 starter credit is used up, or cannot cover the file, without a saved payment method, or paymentFailed when a charge failed. API usage is blocked until a payment method is added. A 409Conflict, treated as not ready yet. download error can mean media is still processing or a transcript is pending. Transcript requests return TRANSCRIPT_PENDING (409) or TRANSCRIPT_UNAVAILABLE (404), with transcription.status in the error body. Inspect the error code to distinguish these from other 409 or 404 errors. Poll progress and retry pending transcript downloads explicitly.

    Restore helpers

    restoreAudio({ downloadType: "transcript" }) in Node and restore_audio(downloadType="transcript") in Python attempt a transcript download after media completion. They do not wait for pending transcription. With the default raiseOnError set to false, they return no bytes and preserve statusCode and responseBody in the returned metadata. Its status can still be complete. With raiseOnError true, they throw DiffioApiError with that metadata attached as restoreInfo. Use progress polling and the download method to retry a pending transcript for the same generation.

Manage projects and generations

Use list endpoints to build dashboards, resume work, or review past generations.

  1. 1

    List projects

    Python
    projects = client.projects.list()for project in projects.projects:    print(project.apiProjectId, project.status, project.originalFileName)
  2. 2

    List generations in a project

    Python
    generations = client.projects.list_generations(    apiProjectId=project.apiProjectId,)for generation in generations.generations:    print(generation.generationId, generation.status, generation.modelKey)

Account, keys, and usage

Agent keys and scoped keys can read account settings, manage API keys, and inspect usage when their scopes allow it.

  1. 1

    Read account settings

    Python
    settings = client.account.get_settings()print(settings.account.get("billingPolicy"))
  2. 2

    Manage API keys

    Python
    created_key = client.api_keys.create(    label="worker-key",    scopes=["projects:read", "projects:write", "generations:read", "generations:write"],)keys = client.api_keys.list()print(created_key.keyId, len(keys.keys))
  3. 3

    Read usage summary

    Python
    usage = client.usage.summary()print(usage.usage, usage.billing)

Restore audio in one call

The audio isolation helper creates the project, starts a generation, waits for completion, and downloads the restored audio in one call.

  1. 1

    Restore in one call

    Python
    from dotenv import load_dotenvfrom diffio import DiffioClientimport osload_dotenv()client = DiffioClient(apiKey=os.getenv("DIFFIO_API_KEY"))file_path = "sample.wav"audio_bytes, info = client.audio_isolation.restore_audio(    filePath=file_path,    model="diffio-4.5-flash",    downloadType="mp3",)if info["error"]:    raise SystemExit(info["error"])with open("restored-one-call.mp3", "wb") as handle:    handle.write(audio_bytes)
    Metadata

    The helper returns a metadata object with status, errors, and download info you can log or store alongside the result.

Use webhooks for status updates

Webhooks let Diffio notify your service when a generation changes status. This is a condensed walkthrough. For full details, see the Webhooks guide.

  1. 1

    Create a webhook endpoint

    Follow the webhook creation steps in the docs, then open the developer dashboard to add your endpoint URL and choose events. Save the signing secret for verification.

    Endpoint scope

    Each API key owns its own webhook endpoints and signing secrets.

    Python
    result = client.webhooks.configure(    mode="live",    url="https://example.com/webhooks/diffio",    eventTypes=["generation.completed", "generation.failed"],    apiKeyId=os.getenv("DIFFIO_API_KEY_ID"),)print(result.webhook)
  2. 2

    Verify webhook signatures

    Use the Diffio SDK helpers to verify signatures against the raw request body, not a parsed JSON object.

    Python
    from fastapi import FastAPI, Request, HTTPExceptionfrom diffio import DiffioClientimport osapp = FastAPI()client = DiffioClient(apiKey=os.environ["DIFFIO_API_KEY"])@app.post("/webhooks/diffio")async def diffio_webhook(request: Request):    payload = await request.body()    try:        event = client.webhooks.verify_signature(            payload=payload,            headers=request.headers,            secret=os.environ["DIFFIO_WEBHOOK_SECRET"],        )    except Exception:        raise HTTPException(status_code=400, detail="Invalid signature")    print("Webhook received", event.eventType)    return {"ok": True}
  3. 3

    Send a test event

    Use an Agent key or a scoped key with webhooks:write to send a test event and confirm delivery in the portal. If you use an Agent key, pass the target apiKeyId.

    Python
    from dotenv import load_dotenvfrom diffio import DiffioClientimport osload_dotenv()client = DiffioClient(apiKey=os.getenv("DIFFIO_WEBHOOK_KEY"))result = client.webhooks.send_test_event(    eventType="generation.completed",    mode="live",    apiKeyId=os.getenv("DIFFIO_API_KEY_ID"),)print(result.svixMessageId)
  4. 4

    Handle webhook events

    When you receive generation.completed, fetch the download URL and queue any heavy work. The transcript is ready when generation.completed arrives.

    Python
    from dotenv import load_dotenvfrom diffio import DiffioClientimport osload_dotenv()client = DiffioClient(apiKey=os.getenv("DIFFIO_API_KEY"))def handle_event(event):    if event.eventType != "generation.completed":        return    download = client.generations.get_download(        generationId=event.generationId,        apiProjectId=event.apiProjectId,        downloadType="audio",    )    print("Download URL:", download.downloadUrl)
    Respond quickly

    Return a 200Success, treated as complete. response fast and push heavy work to a queue or background job.