Skip to content

Files

A file is one of your inputs: a recording you uploaded, or one we imported for you. Projects are made of files. You can make a file on its own (POST /v1/files) and use it in a project later, or make it inline when you create the project (files: [{"fileName": "host.wav"}]): it’s the same thing either way.

Terminal window
curl https://api.podsworth.com/v1/files \
-H "Authorization: Bearer $PODSWORTH_KEY" -H "Content-Type: application/json" \
-d '{"fileName": "host.wav"}'
{
"id": "file_8c1d4f0e2b7a49d1a3f0",
"fileName": "host.wav",
"status": "awaiting_upload",
"uploadUrl": "https://…s3.us-east-2.amazonaws.com/uploads/…?X-Amz-Signature=…",
"uploadExpiresAt": "2026-10-07T18:02:11Z"
}

PUT the file’s bytes to uploadUrl, as the whole request body:

Terminal window
curl -T host.wav "<uploadUrl>"
with open("host.wav", "rb") as f:
requests.put(file["uploadUrl"], data=f).raise_for_status()
await fetch(file.uploadUrl, { method: "PUT", body: await readFile("host.wav") });
  • Why a separate URL: the bytes go straight to our storage instead of through the API, so files up to 5 GB upload as fast as your connection allows. The URL is signed: it needs no Authorization header (or any other header), and works for three hours. Read the file again for a new one.
  • From a browser: the upload URL accepts a PUT from any origin, so your own web app can upload directly (fetch or XMLHttpRequest, which reports progress).
  • Its answers come from storage, not from our API: 200 when it’s in, and an XML error otherwise. A 403 with Request has expired means the URL is too old: read the file for a fresh one and PUT again.
  • Limits: 5 GB per file (500 MB in test mode); at most 100 files waiting for their upload per account. Accepted: WAV, MP3, M4A, MP4, AVI, FLAC, OGG, AAC and a few more; we read the file itself, not its name.

As soon as a file lands we check it: we measure it, and it has to be audio or video we can read. Then it’s ready, with its length, or failed, with why.

status
awaiting_uploadMade; PUT it to uploadUrl.
importingWe’re fetching it from its link.
checkingIt’s in; we’re measuring it (a few seconds).
readydurationSeconds, channels, sampleRate and sizeBytes are filled in, and for a stereo file twoSpeakers (a different speaker on each side: two tracks in a project, see Projects). Usable in projects.
failederror.code (unreadable_file, file_too_large in test mode, import_failed) and error.message.
expiredDeleted 30 days after it was checked.
deletedYou deleted it.

We notice an upload when you next read the file or a project that has it, or straight away if a submitted project is waiting for it, so you never need to tell us you’re done. A file that’s in can’t change: PUTting to its old URL afterwards changes nothing we read or process. To use a different recording, make a new file.

A project of ready files shows its price (creditsSeconds) before you submit it. file.ready and file.failed webhooks tell you when a file is checked.

We fetch the file for you. Imports start as soon as the file is made (status: "importing", no uploadUrl), and it’s checked like an upload once it’s in. A project can be submitted before its imports finish: it waits for them.

source
{"type": "url", "url": "https://…"}Any public https file, e.g. a signed link from your own storage.
{"type": "dropbox", "url": "https://www.dropbox.com/…"}A Dropbox share link.
{"type": "googleDrive", "fileId": "…", "accessToken": "…"}A Google Drive file, with an OAuth access token that can read it (the drive.file scope covers files the user picked). We use the token once and don’t keep it.
{ "fileName": "host.wav", "source": { "type": "url", "url": "https://files.example.com/ep12/host.wav?sig=…" } }

Rules for imports:

  • https only, and the host must resolve to a public address. We follow up to five redirects, checking each one.
  • Dropbox imports stay on Dropbox’s hosts and Drive imports on Google’s.
  • Up to 5 GB per file (500 MB in test mode), and an hour per download.
  • At most 24 files importing at once per account (429 import_limit).

When an import fails because of its link (a 4xx answer, too large, a private address), the file is failed with import_failed and the reason in error.message. Put a new file with a working link in the project (changing a project).

A file isn’t tied to one project: put its id in any project of the same account and mode, including a copy made to process a project again, with no new upload. Files belong to the key’s mode (live and test never see each other’s), and a show’s key sees only the files it made.

{ "name": "Episode 12 (merged)", "files": ["file_8c1d4f0e2b7a49d1a3f0", "file_93ab07c2d4e14f5a8b61"] }
GET /v1/files?status=ready&limit=50&cursor=…
GET /v1/files/{id}
DELETE /v1/files/{id}

Lists are newest first, at most 100 per page; pass nextCursor as cursor for the next page.

Files are deleted 30 days after they’re checked (expiresAt), unless a kept project uses them: then they’re kept too, and expiresAt is null. DELETE /v1/files/{id} deletes one now, kept copy included. It’s refused with 409 file_in_use while a submitted project that hasn’t finished uses it.