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.
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
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).
.envDIFFIO_API_KEY=diffio_live_...TipStore your API key as a managed secret instead of committing it to source control.
- 2
Install the SDK
Install the Diffio Python or Node SDK, then load your API key from an environment file.
Pythonpip install diffio python-dotenvSDK source codeBrowse the SDK repositories on GitHub: diffio-js and diffio-python .
Upgrading an existing integrationDiffio 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. Usediffio-4.5-flashordiffio-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) anddiffio-4.5-proare accepted.diffio-2,diffio-2-flash,diffio-3.4,diffio-3.5,diffio-4.0-flash, anddiffio-4.0-proare 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, andbucket, and addsupload(the upload session) anduploadCompletion. - The download response drops
bucket. - Node: upload failures raise
DiffioUploadError, andwaitForGenerationwaits up to 600 seconds by default instead of 60.
NoteUse Node 18 or later so fetch is available without extra packages.
- Only
- 3
Configure the client
Create a file named
full_guide.pyorfull_guide.mjs. Then initialize the client and set global request options like timeouts and retries.Pythonfrom 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 overridesEvery SDK call accepts
requestOptionsso you can override timeouts, retries, headers, or the API key for a single request. Generation creation requires a nonblankidempotencyKeybefore automatic retries are allowed. - 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.Pythonfile_path = "sample.wav"project = client.create_project( filePath=file_path, fileFormat="wav", params={ "source": "full-guide", "notes": "First upload from docs", },)How the upload workscreate_projectandcreateProjectupload 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 hasupload(the session) anduploadCompletion(statusandsizeBytes). Files larger than 2 GiB are refused bycreate_projectwith413Client error, treated as fix required. and codeUPLOAD_TOO_LARGEbefore any bytes are sent.Python sends one part at a time and retries a failed part on its own only when
maxRetriesis set. An error response from the upload API raisesDiffioApiError; network errors raise the underlyinghttpxexception. 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 ofmaxRetries. It raisesDiffioUploadErrorwithuploadErrorCode,edgeErrorCode, andapiProjectId. Node leaves the upload token out of the returnedupload; Python keeps it asupload.uploadToken.If the upload finished but the confirmation call failed, confirm it with
client.complete_project_upload(apiProjectId=...)orclient.projects.complete_uploadin Python, orclient.completeProjectUploadorclient.projects.completeUploadin Node. The call is idempotent. - 5
Create a generation
Start a generation with the model you want. Each Diffio 4.5 model uses its own fixed sampling profile, so
samplingandparamsdo not change the result; the SDKs still accept them, and the API stores them with the generation. The optionalidempotencyKeymakes retries safe; repeating a request with the same key returns the existing generation instead of creating and billing a duplicate. The response includesidempotentReplayset 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-callmaxRetriesenables 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.Pythongeneration = client.generations.create( apiProjectId=project.apiProjectId, model="diffio-4.5-flash", idempotencyKey="docs-guide-restore-1",) - 6
Wait for completion
Poll until the overall
statusiscomplete. The wait helper returns the full progress object. Individual stages reaching 100% orcompletedo not end polling while required media artifacts or usage settlement are still pending. A terminalfailedstatus causes the wait helper to raise an error. Both SDKs wait up to 600 seconds by default; passtimeoutin Python ortimeoutInSecondsin Node to change it.Progress also reports
stage(the step the generation is in, such asqueued,restoring, oruploading),stageProgress(percentages and byte counts while a processing worker runs it), andqueue(position and a message while it waits for a worker). Each isNonein Python orundefinedin Node when the API does not report it.Pythonprogress = 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 transcriptionDiffio 4.5 transcribes the recording before restoration starts, so a
completegeneration always has its transcript. Readprogress.transcription.statusforpending,available, orunavailablewhile a generation is running.This applies to
waitForGeneration,wait_for_generation,generations.waitForComplete, andgenerations.wait_for_complete. Older responses omittranscription; SDKs expose that absence asundefinedin Node orNonein Python. Check for it before readingstatus.Media recoveryTemporary 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
failedand its pending usage reservation is released. Permanent input errors can fail immediately.ShortcutYou can combine creation and polling with
client.generations.create_and_waitin Python orclient.generations.createAndWaitin Node. - 7
Download results
Save the restored audio, then request any available transcript JSON.
downloadUrlis a signed Diffio media URL that needs noAuthorizationheader 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.Pythonclient.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: raiseTroubleshootingIf you receive
401Unauthorized, treated as auth error. or403Forbidden, treated as permission error., confirm the API key is valid and has read and write permissions. A402Payment required, billing issue such as paymentFailed. API usage can be blocked until billing is updated. error indicates a billing issue:needsPaymentMethodwhen the $5 starter credit is used up, or cannot cover the file, without a saved payment method, orpaymentFailedwhen a charge failed. API usage is blocked until a payment method is added. A409Conflict, treated as not ready yet. download error can mean media is still processing or a transcript is pending. Transcript requests returnTRANSCRIPT_PENDING(409) orTRANSCRIPT_UNAVAILABLE(404), withtranscription.statusin 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 helpersrestoreAudio({ downloadType: "transcript" })in Node andrestore_audio(downloadType="transcript")in Python attempt a transcript download after media completion. They do not wait for pending transcription. With the defaultraiseOnErrorset to false, they return no bytes and preservestatusCodeandresponseBodyin the returned metadata. Itsstatuscan still becomplete. WithraiseOnErrortrue, they throwDiffioApiErrorwith that metadata attached asrestoreInfo. 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
List projects
Pythonprojects = client.projects.list()for project in projects.projects: print(project.apiProjectId, project.status, project.originalFileName) - 2
List generations in a project
Pythongenerations = 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
Read account settings
Pythonsettings = client.account.get_settings()print(settings.account.get("billingPolicy")) - 2
Manage API keys
Pythoncreated_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
Read usage summary
Pythonusage = 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
Restore in one call
Pythonfrom 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)MetadataThe 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
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 scopeEach API key owns its own webhook endpoints and signing secrets.
Pythonresult = 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
Verify webhook signatures
Use the Diffio SDK helpers to verify signatures against the raw request body, not a parsed JSON object.
Pythonfrom 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
Send a test event
Use an Agent key or a scoped key with
webhooks:writeto send a test event and confirm delivery in the portal. If you use an Agent key, pass the targetapiKeyId.Pythonfrom 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
Handle webhook events
When you receive
generation.completed, fetch the download URL and queue any heavy work. The transcript is ready whengeneration.completedarrives.Pythonfrom 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 quicklyReturn a
200Success, treated as complete. response fast and push heavy work to a queue or background job.
