Skip to main content

Errors

Speech is Cheap returns the messages on this page word for word. Search the page for the text you got, or for the HTTP status code. Most errors are a plain-text body with a standard HTTP status code. The Upload service and a few x402 responses return JSON. A job that the API accepts can still fail later. You see that failure as a FAILED job: in your webhook, and for x402 jobs also in output.error when you get the job. See Failures After a Job Is Accepted. You aren't charged for a request that fails or a job that doesn't complete; x402 refunds the payment in those cases.

Jobs API​

Errors from https://api.speechischeap.com/v2/jobs/. The body is plain text. The Upload and Edge APIs return some of the same messages.

Speech is Cheap: Unauthorized API Key. Sign up at https://speechischeap.com/ and use code E401 for a special discount while supplies last.​

Status: 401 Unauthorized

Cause: The API couldn't match the request to an active API key. The message is the same for every cause:

  • The request has no Authorization header.
  • The key has a typo, was never issued, was revoked, or has expired.
  • The key lookup failed for a moment. This is rare.

How to fix: Send Authorization: Bearer YOUR_API_KEY. New keys start with sk_. To test a key without creating a job, send it to the check endpoint. A working key returns Authenticated:

curl https://api.speechischeap.com/v2/jobs/auth \
--header 'Authorization: Bearer YOUR_API_KEY'

Create or revoke keys in the Dashboard. If you don't have an account yet, start at Pricing.

Speech is Cheap: Invalid authorization header. Format: "Authorization: Bearer <key>"​

Status: 401 Unauthorized

Cause: The Authorization header isn't Bearer followed by a space and a key. One of:

  • The header is just Bearer with no key after it. This happens when an environment variable that holds the key is empty.
  • The header uses another scheme, such as Basic, or holds the bare key.

How to fix: Send Authorization: Bearer YOUR_API_KEY, with one space after Bearer. Check that your key variable is set.

Speech is Cheap: Missing subscription. Manage your subscriptions at https://billing.speechischeap.com/.​

Status: 402 Payment Required

Cause: The API key is valid, but its billing isn't active. For example, a payment is past due, or the plan (monthly subscription or pay as you go) was canceled.

How to fix: Update your billing or pick a plan in Dashboard › Billing, then retry. The link in the message opens Stripe's billing sign-in. If your billing is in good standing, email support@speechischeap.com.

Invalid or missing JSON payload​

Status: 400 Bad Request

Cause: The request body isn't valid JSON, or it's empty. The API reads the body as JSON whatever the Content-Type header says.

How to fix: Send a JSON object, such as {"input_url": "https://example.com/audio-file.mp3"}. To send a file, use Upload a File instead.

Missing required "input_url" parameter​

Status: 400 Bad Request

Cause: The body has no input_url, or it's empty. The API ignores fields it doesn't know, so a misspelled name counts as missing.

How to fix: Add input_url. See Create a Job from a URL.

Ambiguous "output_format" parameter. Expected "srt", "vtt", or "webvtt"​

Status: 400 Bad Request

Cause: output_format is set to something other than srt, vtt, or webvtt. Letter case doesn't matter.

How to fix: Use one of those three values, or leave output_format out.

Input URL is not reachable: <url> (HTTP <n>)​

Status: 400 Bad Request

Cause: The URL failed the reachability check. The end of the message tells you what happened. <n> is the status of the last request, which is the GET request when HEAD got an error:

  • (HTTP 401) or (HTTP 403): the server refuses the request. The file needs a login, the host blocks unknown clients, or a pre-signed URL expired.
  • (HTTP 404) or (HTTP 410): nothing is at that URL any more.
  • (HTTP 500) or another 5xx status: the host has an error.
  • (network error): no response came back. The host name doesn't resolve, the connection or TLS failed, the host took longer than 10 seconds, or the value isn't an http or https URL.

How to fix: Open the URL in a private browser window or with curl --head. It must answer without a login. Renew an expired pre-signed URL. If the host blocks unknown clients, set user_agent to a value it accepts. Then create the job again.

For a private job (is_private), <url> shows as https://speechischeap.com/redacted. If you set webhook_url, the API also posts a FAILED callback with this message. An upload that fails this check reports it as API call failed: 400 - Input URL is not reachable.

Failed to start a new transcribe job​

Status: 500 Internal Server Error

Cause: The transcription service didn't accept the job, so no job was created and nothing is billed. If you set webhook_url, the API also posts a FAILED callback with this message.

How to fix: Send the request again. If it keeps failing, email support@speechischeap.com.

Failed to create a new job database entry for "<url>"​

Status: 500 Internal Server Error

Cause: The API couldn't save the job record. It doesn't track this attempt, so the attempt never delivers a result and isn't billed. For a private job, <url> shows as https://speechischeap.com/redacted.

How to fix: Send the request again. If it keeps failing, email support@speechischeap.com.

Something went wrong​

Status: 500 Internal Server Error

Cause: An unexpected error. The message doesn't say which. Check these in order:

  • On POST, the API may have failed to hand the job to the transcription service. No job exists and nothing is billed.
  • On any request, a temporary failure inside the API.

How to fix: Retry after a few seconds. If the same request keeps failing, email support@speechischeap.com with the time of the request and its time zone, the endpoint, and the job ID if you have one.

Invalid output_format specified.​

Status: 400 Bad Request, on GET /v2/jobs/<id>

Cause: The output_format query parameter isn't srt, vtt, or webvtt, and the job is COMPLETED. For a job that isn't complete, the API ignores output_format and returns the job's JSON status.

How to fix: Use srt, vtt, or webvtt, or leave the parameter out. See Get a Job.

404 Not Found​

Status: 404 Not Found

Cause: This message is generic. Check it by request:

  • GET /v2/jobs/<id>: no job has that ID. Look for a typo or a cut-off ID. Job IDs end in -sic. A trailing slash after the ID also returns 404.
  • DELETE /v2/jobs/<id>: the job doesn't exist, it belongs to another account, or it already ended as COMPLETED, FAILED, or CANCELED. The API gives the same answer for all three. Get the job to tell them apart. If the GET also returns 404, the ID is wrong. If it shows PENDING, the job belongs to another account, so use the key that created it. If it shows an ended status, there's nothing left to cancel.
  • Any other request: the path or method doesn't exist.

How to fix: Use job IDs exactly as the API returned them, and the paths listed in the Jobs API overview.

Internal Server Error​

Status: 500 Internal Server Error

Cause: This message is generic. The API failed to read or update a job:

  • A temporary database or storage failure. A retry usually works.
  • A completed job whose saved transcript can't be loaded. This one fails again for the same job ID.

How to fix: Retry after a few seconds. If the same job ID keeps failing, email support@speechischeap.com with the job ID.

Paid jobs require the original wallet cancellation proof​

Status: 403 Forbidden

Cause: You sent DELETE /v2/jobs/<id> with an API key for a job that was paid for with x402. Only the paying wallet can cancel it.

How to fix: Cancel the job on the x402 endpoint with a wallet signature. See Cancel a Job.

Speech is Cheap: Jobs v1 has been retired. Use Jobs v2: https://docs.speechischeap.com/jobs-v2/create/​

Status: 410 Gone

Cause: The request went to a /v1/jobs path. Those endpoints no longer exist.

How to fix: Send the request to POST https://api.speechischeap.com/v2/jobs/. See Create a Job from a URL.

Upload​

Errors from https://upload.speechischeap.com. The API-key route is POST /v2/jobs/ and the x402 route is POST /v2/x402/jobs/. Error bodies are JSON. Unless an entry says otherwise, a 400 from the API-key route has the body {"message": "…"}, and a failure from the x402 route has an error field. Replayed x402 failures also include status and purchase_id, and a few early x402 failures, such as a missing token, have only error.

Both routes answer a new upload with an event stream and HTTP 200. A failure after the stream starts can't change the status code, so it arrives as an event, {"status": "error", "error": "…"}. On the x402 route the event also has a purchase_id. A failure before the stream starts returns the HTTP status shown in its entry. On the x402 route, an upload that fails while the service receives the file is refunded, and its token can't be used again. A retry with the same token returns 410 with the original message.

Missing Authorization header​

Status: 401 Unauthorized, with the body {"error": "Missing Authorization header"}

Cause: The API-key route got no Authorization header.

How to fix: Send Authorization: Bearer YOUR_API_KEY.

Speech is Cheap: Unauthorized API Key. Sign up at https://speechischeap.com/ and use code E401 for a special discount while supplies last.​

Status: 401 Unauthorized, with the body {"error": "…"}

Cause: The Jobs API rejected your key. The causes are the same as in the Jobs API entry.

How to fix: See the Jobs API entry.

Speech is Cheap: Invalid authorization header. Format: "Authorization: Bearer <key>"​

Status: 401 Unauthorized, with the body {"error": "…"}

Cause: The Authorization header isn't Bearer followed by a space and a key. See the Jobs API entry.

How to fix: Send Authorization: Bearer YOUR_API_KEY.

Speech is Cheap: Missing subscription. Manage your subscriptions at https://billing.speechischeap.com/.​

Status: 401 Unauthorized, with the body {"error": "…"}. Upload returns the Jobs API's 402 as a 401.

Cause: The API key is valid, but its billing isn't active. See the Jobs API entry.

How to fix: Follow the fix in the Jobs API entry.

Unauthorized​

Status: 401 Unauthorized, with the body {"error": "Unauthorized"}

Cause: A check of the same Authorization header failed in the last 60 seconds. Upload remembers a failed check for 60 seconds and answers with this short message.

How to fix: Fix the key or its billing, wait a minute, and send the request again.

Authentication service unavailable​

Status: 401 Unauthorized, with the body {"error": "Authentication service unavailable"}

Cause: Upload couldn't reach the Jobs API to check your key.

How to fix: Send the request again in a few seconds.

the request is not multipart​

Status: 406 Not Acceptable. The body includes the code FST_INVALID_MULTIPART_CONTENT_TYPE.

Cause: The API-key route needs a multipart/form-data body. You sent JSON, plain text, or an empty body. Other content types get the 415 below.

How to fix: Send the file as a form field named input_file, for example with curl --form 'input_file=@audio.mp3'. See Upload a File. The x402 route returns 415 with a different message: Expected a multipart input_file upload.

Unsupported Media Type​

Status: 415 Unsupported Media Type, with the body {"statusCode": 415, "code": "FST_ERR_CTP_INVALID_MEDIA_TYPE", "error": "Unsupported Media Type", "message": "Unsupported Media Type"}.

Cause: The request has a Content-Type that Upload doesn't read, such as application/x-www-form-urlencoded or application/octet-stream, or it has a body and no Content-Type. Upload answers before it checks the API key.

How to fix: Send a multipart/form-data body with the file in input_file, for example with curl --form 'input_file=@audio.mp3'.

Missing required "input_file" parameter​

Status: 400 Bad Request

Cause: The multipart body has no file.

How to fix: Add the file as a form field named input_file. On the x402 route the message ends with a period.

Invalid field name for file upload. Expected 'input_file'.​

Status: 400 Bad Request

Cause: The file field has another name, such as file. The service reads the first file in the body.

How to fix: Name the file field input_file.

Upload ended before the file was complete.​

Status: 400 Bad Request

Cause: The request body stopped inside the file. The client stopped sending, or something between you and the service cut the request.

How to fix: Upload again over a stable connection. After a paid x402 upload fails this way, request a new quote.

Unsupported file type detected: <type>.​

Status: 400 Bad Request

Cause: The file's content isn't one of the accepted types. <type> is the detected MIME type, or unknown when the content isn't recognized or the file is empty. The check reads the content, so renaming the file doesn't help.

How to fix: Convert the file to a supported format. The x402 route says Unsupported file type detected. without a type, and it refunds the payment.

Upload connection closed​

Status: An event in the stream. HTTP 200 was already sent.

Cause: The connection dropped, or the request ended before every byte arrived.

How to fix: Upload again. On the x402 route this appears as Upload processing failed.

API call failed: 400 - Input URL is not reachable: <url> (HTTP 400)​

Status: An event in the stream. HTTP 200 was already sent.

Cause: The upload finished and passed the content check, but the file's extension isn't one the service can serve to the transcriber. The service keeps the extension from your file name, and it serves only files that end in .mp3, .wav, .m4a, .flac, .ogg, .opus, .webm, or .mp4. A file named talk.m4b, talk.mpeg, or just talk fails this way.

How to fix: Rename the file with a supported extension and upload it again. See Uploads.

API call failed: <status> - <body>​

Status: An event in the stream. HTTP 200 was already sent.

Cause: The upload finished, but the Jobs API refused to create the job. The text after the dash is the Jobs API's own response, so look it up in the Jobs API section. For example, API call failed: 400 - Ambiguous "output_format" parameter. … means a form field had an invalid value.

How to fix: Follow the fix for that Jobs API message, then upload again.

"status": "error"​

Status: An event in the stream, or 500 Internal Server Error with the body {"error": "…"} when the stream hasn't started

Cause: An API-key upload failed for a reason that has no entry above. The error text is the underlying error from the upload service, such as a file-system error while it stored the file. This is a problem on the service side.

How to fix: Upload the file again. If it keeps failing, email support@speechischeap.com with the time of the request and its time zone.

Missing X-SiC-Upload-Token header.​

Status: 401 Unauthorized, with the body {"error": "Missing X-SiC-Upload-Token header."}

Cause: The x402 route needs the upload token from your paid response in the X-SiC-Upload-Token header. It doesn't accept an API key.

How to fix: Send the upload_token you received as X-SiC-Upload-Token. See Upload a File.

Expected a multipart input_file upload.​

Status: 415 Unsupported Media Type, with the body {"error": "Expected a multipart input_file upload."}

Cause: The body isn't multipart/form-data.

How to fix: Send the file as a form field named input_file.

This upload is already in progress.​

Status: 409 Conflict, with the body {"error": "This upload is already in progress."}

Cause: Another request is still using this upload token, or an earlier attempt hasn't ended.

How to fix: Wait for the first request to finish, then send the same token again. If the earlier upload finished, you get its job back.

Purchase service rejected this upload operation.​

Status: 404, 409, 410, or 503

Cause: The payment service refused your upload token. The status tells you why:

  • 404: the token is unknown.
  • 409: the purchase can't take this upload in its current state, for example because it already failed.
  • 410: the purchase's window ended. You have 300 seconds after payment to start the upload and one hour to finish it. The payment is refunded.
  • 503: the payment service had an internal error.

How to fix: For 404 and 409, check that you send the upload_token from your paid response unchanged. For 410, request a new quote, pay, and upload again. For 503, send the same request again with the same token.

Purchase service unavailable. Retry this upload token.​

Status: 503 Service Unavailable

Cause: The upload service couldn't reach the payment service, or the payment service had a temporary problem.

How to fix: Send the same request again with the same token. The token stays valid.

Purchase response unavailable. Retry this upload token.​

Status: 503 Service Unavailable

Cause: The payment service's reply couldn't be read.

How to fix: Send the same request again with the same token.

Purchase service returned an invalid upload claim.​

Status: 503 Service Unavailable

Cause: The payment service's answer to the upload claim was incomplete. The upload service stored nothing.

How to fix: Send the same request again with the same token. If it keeps failing, email support@speechischeap.com.

Prepaid uploads are not configured.​

Status: 503 Service Unavailable

Cause: The upload service isn't set up to take x402 uploads right now. Your request isn't the problem.

How to fix: Try again later. If it keeps failing, email support@speechischeap.com.

The Worker has not resolved this upload outcome. Retry this upload token.​

Status: 503 Service Unavailable

Cause: An earlier upload with this token failed, and the service is still confirming the failure and your refund with the payment service.

How to fix: Wait a few seconds and send the same token again. When the failure is confirmed, the reply becomes a 410.

Upload saved. Retry this upload token to recover the job.​

Status: An event in the stream. On a retry, a 5xx status instead.

Cause: The file arrived and is stored, but the service couldn't confirm the job with the payment service. The reply may have been lost, so the job may already have started.

How to fix: Send the same request again with the same token. The service uses the stored file and returns the job. You aren't charged twice.

The accepted upload could not be submitted.​

Status: An event in the stream after HTTP 200. A retry with the same token returns 410 Gone.

Cause: The file was stored, but the payment service ended the purchase before the job could start, for example because the upload window closed. The payment is refunded.

How to fix: Request a new quote, pay, and upload again.

The accepted upload did not complete.​

Status: 410 Gone, on a retry with the same token

Cause: An earlier upload with this token never finished. The transfer was interrupted, or it ran for more than one hour. The failure was reported and the payment is refunded.

How to fix: Request a new quote, pay, and upload again.

Uploaded file exceeds the size limit.​

Status: An event in the stream. HTTP 200 was already sent. A retry with the same token returns 410.

Cause: The file is larger than 2 GiB. The payment is refunded.

How to fix: Compress or split the audio so each file is under 2 GiB. Then request a new quote, pay, and upload again.

Expected at least 6 seconds; got 0 seconds​

Status: 400 Bad Request

Cause: The uploaded file is empty. The payment is refunded. A non-empty file shorter than 6 seconds fails after the job is accepted instead: see Expected at least 6 seconds; got <n> seconds.

How to fix: Send a file with at least 6 seconds of audio. Request a new quote first.

Upload processing failed.​

Status: 500 Internal Server Error, or an event in the stream

Cause: An unexpected error while the service received or stored the file, for example a dropped connection. The service reports the failure, so the payment is refunded.

How to fix: Request a new quote and upload again. If it keeps failing, email support@speechischeap.com.

Invalid file ID​

Status: 400 Bad Request, with the body {"error": "Invalid file ID"}, on GET or HEAD of an uploaded file's URL

Cause: The stored file's name doesn't end in a supported extension. This is what makes the Jobs API report Input URL is not reachable … (HTTP 400) for an upload.

How to fix: Upload the file again with a supported extension. See API call failed: 400 - Input URL is not reachable.

File not found​

Status: 404 Not Found, with the body {"error": "File not found"}

Cause: No stored file has that name. The service deletes uploaded files after about an hour.

How to fix: Upload the file again, and create the job right after the upload finishes.

Range not satisfiable​

Status: 416 Range Not Satisfiable, with the body {"error": "Range not satisfiable"}

Cause: A Range header asked for bytes beyond the end of the file.

How to fix: Request a byte range inside the file's size.

x402 API​

Errors from POST https://api.speechischeap.com/v2/x402/jobs/, which handles quotes and payments, and from DELETE /v2/x402/jobs/<id>, which cancels a job. Upload errors are under Upload. Failures after the service accepts your payment are under Failures After a Job Is Accepted. Bodies are plain text unless an entry says otherwise.

A 402 isn't always an error. A request without a PAYMENT-SIGNATURE header gets a 402 with a PAYMENT-REQUIRED header. That's your quote. See Payments and Refunds. When you get one of these errors, no payment was taken unless the entry says otherwise.

x402 payments are not enabled​

Status: 503 Service Unavailable

Cause: The service isn't accepting new x402 payments at the moment. A payment it already accepted is still completed or refunded.

How to fix: Don't retry in a loop. Try again later, or use an API key in the meantime.

x402 payment configuration is incomplete​

Status: 503 Service Unavailable

Cause: The service can't take payments right now. Your request isn't the problem.

How to fix: Try again later. If it lasts, email support@speechischeap.com.

Request body must be at most 16 KB​

Status: 413 Content Too Large

Cause: The request body is larger than 16 KB. Quote requests are small JSON objects.

How to fix: Remove fields you don't need and shorten long values. input_url and webhook_url can be up to 2,048 characters.

Invalid or missing JSON payload​

Status: 400 Bad Request

Cause: The request body isn't valid JSON, or it's empty.

How to fix: Send a JSON object. See Create a Job from a URL.

Expected a JSON object​

Status: 400 Bad Request

Cause: The body is valid JSON but not an object. It's an array, a string, a number, or null.

How to fix: Send a JSON object.

Unsupported parameter "<name>"​

Status: 400 Bad Request

Cause: The request has a field that the x402 API doesn't accept. Unlike the Jobs API, x402 rejects unknown fields.

How to fix: Remove the field named in the message, or fix its spelling. See Create a Job from a URL for the accepted parameters.

input_duration must be a JSON number from 6 to 86400 seconds​

Status: 400 Bad Request

Cause: input_duration is missing, is not a JSON number ("200" with quotes is a string), or is outside 6 to 86400.

How to fix: Send the length of your audio in seconds, for example "input_duration": 200.

input_type must be "url" or "upload"​

Status: 400 Bad Request

Cause: input_type has another value.

How to fix: Use url, upload, or leave it out for a URL job.

Missing or invalid "input_url"​

Status: 400 Bad Request

Cause: A URL quote has no input_url, or it isn't a non-empty string.

How to fix: Add input_url, or set input_type to upload.

<field> must be an HTTP or HTTPS URL without credentials​

Status: 400 Bad Request

Cause: <field> is input_url or webhook_url. The value isn't an http or https URL, or it contains a user name or password.

How to fix: Use a plain http or https URL. For a private file, use a pre-signed URL instead of embedding credentials.

Upload quotes must not include input_url​

Status: 400 Bad Request

Cause: The quote has input_type set to upload and also has input_url.

How to fix: Remove input_url. You send the file after you pay. See Upload a File.

Invalid "<field>" parameter​

Status: 400 Bad Request

Cause: A true-or-false field isn't true or false. Or language, user_agent, output_format, or webhook_url isn't a string.

How to fix: Send the type the message names. See Create a Job from a URL.

x402 supports asynchronous jobs only; can_stream_output must be false​

Status: 400 Bad Request

Cause: The quote sets can_stream_output to true. x402 jobs don't stream.

How to fix: Remove can_stream_output. Get the result with a webhook or by getting the job.

"<field>" must be at most <n> characters​

Status: 400 Bad Request

Cause: A value is too long. input_url and webhook_url can have up to 2,048 characters, user_agent up to 256, and language up to 16.

How to fix: Shorten the value named in the message.

minimum_confidence must be a number from 0 to 1​

Status: 400 Bad Request

Cause: minimum_confidence isn't a JSON number from 0 to 1. The Jobs API checks this only after it accepts a job. x402 checks it when you request the quote.

How to fix: Send a number such as 0.5, which is the default.

segment_duration must be a positive integer​

Status: 400 Bad Request

Cause: segment_duration isn't a whole number greater than zero.

How to fix: Send a whole number of seconds, such as 30.

Ambiguous "output_format" parameter. Expected "srt", "vtt", or "webvtt"​

Status: 400 Bad Request

Cause: output_format is set to something other than srt, vtt, or webvtt.

How to fix: Use one of those three values, or leave output_format out.

Too many payment quotes; retry after 60 seconds​

Status: 429 Too Many Requests, with the JSON body {"message": "…"} and a Retry-After: 60 header

Cause: Your IP address asked for more than about 20 quotes in a minute without paying for them.

How to fix: Wait for the Retry-After time. Pay an open quote instead of requesting new ones. A quote stays valid for 5 minutes.

Invalid PAYMENT-SIGNATURE​

Status: 400 Bad Request

Cause: The PAYMENT-SIGNATURE header isn't a valid x402 v2 payment for the exact scheme on Base (eip155:8453). It's malformed, or it uses another scheme or network.

How to fix: Build the header from the accepts entry of the quote with an x402 client library. See Payments and Refunds.

Payment is missing its quoted resource​

Status: 400 Bad Request

Cause: The payment's resource doesn't point at your quote on this API.

How to fix: Copy the resource from the 402 response unchanged into the payment.

Expected an EIP-3009 authorization​

Status: 400 Bad Request

Cause: The payment has no authorization object.

How to fix: Sign an EIP-3009 transfer authorization for USDC. An x402 client library does this for you.

Invalid EIP-3009 authorization​

Status: 400 Bad Request

Cause: A field of the authorization or its signature has the wrong format. For example, an address, the nonce, or the signature isn't a hex string of the right length.

How to fix: Sign a new authorization with an x402 client library and send it for the same quote.

Payment quote was not found​

Status: 404 Not Found

Cause: No quote matches the purchase_id in the payment's resource. The quote was never issued here, or it expired and was removed.

How to fix: Request a new quote.

Payment quote is bound to different request parameters​

Status: 409 Conflict

Cause: The request body or the payment's accepted terms differ from the quote it pays. Even one changed field counts.

How to fix: Send the same body and the same terms that the quote was issued for. To change anything, request a new quote.

Purchase is bound to another payment​

Status: 409 Conflict

Cause: You sent a different payment authorization for a quote that already has one.

How to fix: Retry with the first PAYMENT-SIGNATURE you sent for this quote.

Purchase payment proof has changed​

Status: 409 Conflict

Cause: The authorization is the same, but the signed payload changed since your first request.

How to fix: Retry with the original PAYMENT-SIGNATURE, unchanged.

Payment authorization already belongs to another purchase​

Status: 409 Conflict

Cause: You already used this authorization, with its nonce, for a different quote.

How to fix: Sign a new authorization with a new nonce for this quote.

Quote expired; request a new quote before payment​

Status: 410 Gone

Cause: The quote is older than 5 minutes, or the service no longer has it. No payment was taken.

How to fix: Request a new quote and pay it within 5 minutes.

Payment authorization ended without settlement; request a new quote before payment​

Status: 410 Gone

Cause: No funds moved. The authorization expired before the service used it, or the payer canceled it.

How to fix: Request a new quote and sign a new authorization.

402 With a New PAYMENT-REQUIRED​

Status: 402 Payment Required, with a PAYMENT-REQUIRED header and an error field in the body

Cause: The service rejected your PAYMENT-SIGNATURE before it moved any money, and the quote is still open. The error field says why:

  • Payment authorization does not match the quote: the recipient or the amount differs from the quote.
  • Payment authorization validBefore must be 6 to 360 seconds from now: the authorization's deadline is too near or too far.
  • A reason code from the payment facilitator, such as insufficient_funds or invalid_exact_evm_payload_signature.
  • Invalid payment authorization: the facilitator gave no usable reason.

How to fix: Fix the problem the error field names, sign a new authorization for the same quote, and send it. No payment was taken, and the service never settles the rejected authorization later. If the quote expired, request a new one.

402 With PAYMENT-RESPONSE and success: false​

Status: 402 Payment Required, with a PAYMENT-RESPONSE header whose success is false

Cause: The payment facilitator declined to settle your authorization. Its errorReason says why, for example insufficient_funds, invalid_exact_evm_payload_signature, kyt_risk_detected, or request_blocked_by_location. No payment was taken, and the service never submits this authorization again.

How to fix: Request a new quote and sign a new authorization. If the reason is insufficient_funds, add USDC on Base to the paying wallet first.

Payment admission is temporarily unavailable; retry the original request and payment proof​

Status: 503 Service Unavailable, with the JSON body {"message": "…"}

Cause: An unexpected error while the service processed your payment. The payment may or may not have settled.

How to fix: Send the same request again with the same PAYMENT-SIGNATURE. Don't sign a new payment. The retry returns the original outcome, and a payment is never taken twice for one authorization.

Wallet authentication required to cancel this job​

Status: 402 Payment Required, with a PAYMENT-REQUIRED header and the JSON body that holds this message

Cause: This is the first step of a cancellation, not a failure. A DELETE without a SIGN-IN-WITH-X header gets a challenge for the paying wallet to sign.

How to fix: Sign the challenge with the wallet that paid, and send the DELETE again with the SIGN-IN-WITH-X header. The challenge is valid for 5 minutes. See Cancel a Job.

Invalid SIGN-IN-WITH-X proof​

Status: 401 Unauthorized

Cause: The SIGN-IN-WITH-X header can't be read as a sign-in proof.

How to fix: Build the header from the challenge with an x402 client library.

Cancellation challenge expired or was already used​

Status: 401 Unauthorized

Cause: The challenge is older than 5 minutes, belongs to another job, or you already used it. Each challenge works once.

How to fix: Send a DELETE without the header to get a new challenge, then sign it.

Signature is not for this job cancellation​

Status: 401 Unauthorized

Cause: The signed message doesn't match the challenge for this job. A field was changed, or the chain isn't Base.

How to fix: Sign the challenge exactly as you received it.

Invalid cancellation challenge​

Status: 401 Unauthorized

Cause: The signed message doesn't meet the sign-in rules for this job, for example its domain or time window.

How to fix: Get a new challenge and sign it unchanged.

Only the original paying wallet can cancel this job​

Status: 403 Forbidden

Cause: The signature is valid, but it's not from the wallet that paid for this job.

How to fix: Sign with the paying wallet.

Paid job not found​

Status: 404 Not Found

Cause: No paid x402 job has that ID. It may be a Jobs API job, a mistyped ID, or a purchase that hasn't produced a job.

How to fix: Use the id from your paid response. Cancel a Jobs API job with Cancel a Job.

"cancellation_status": "terminal"​

Status: 409 Conflict, with the JSON body {"id": "…", "status": "…", "cancellation_status": "terminal"}

Cause: The job already ended, so it can't be canceled. status shows how it ended.

How to fix: Nothing to cancel. Get the job to read the result.

Paid job recovery is temporarily unavailable​

Status: 503 Service Unavailable

Cause: An unexpected error while the service processed your cancellation.

How to fix: Send the same DELETE again. If it keeps failing, email support@speechischeap.com with the job ID.

Edge API​

Errors from https://edge.speechischeap.com/v2/jobs/. The body is plain text, except for a FAILED result, which is JSON with HTTP status 200.

Speech is Cheap: Unauthorized API Key. Sign up at https://speechischeap.com/ and use code E401 for a special discount while supplies last.​

Status: 401 Unauthorized

Cause: The API couldn't match the request to an active API key. The causes are the same as in the Jobs API entry.

How to fix: See the Jobs API entry. Edge accepts the same keys.

Speech is Cheap: Invalid authorization header. Format: "Authorization: Bearer <key>"​

Status: 401 Unauthorized

Cause: The Authorization header isn't Bearer followed by a space and a key. See the Jobs API entry.

How to fix: Send Authorization: Bearer YOUR_API_KEY.

Speech is Cheap: Missing subscription. Manage your subscriptions at https://billing.speechischeap.com/.​

Status: 402 Payment Required

Cause: The API key is valid, but its billing isn't active. See the Jobs API entry.

How to fix: Follow the fix in the Jobs API entry.

Failed to parse request body. Please use multipart/form-data.​

Status: 400 Bad Request

Cause: Edge couldn't read the body as form data. For example, the body is JSON, malformed, or empty. A URL-encoded form body is read, so it gets the missing input_file message below instead.

How to fix: Send a multipart/form-data body with the file in a field named input_file, for example with curl --form 'input_file=@audio.mp3'. See Upload a File.

Missing or invalid "input_file". Please upload an audio file.​

Status: 400 Bad Request

Cause: The form has no field named input_file, or that field holds text instead of a file.

How to fix: Attach the file with --form 'input_file=@audio.mp3'. A plain input_file=audio.mp3 sends text.

Invalid "language" parameter. It must be an ISO 639-1 string.​

Status: 400 Bad Request

Cause: The language field was sent as a file instead of text. Edge doesn't check the value itself.

How to fix: Send language as a plain text field with a code from Supported Languages, or leave it out to detect the language.

Invalid "minimum_confidence" parameter. It must be between 0.0 and 1.0.​

Status: 400 Bad Request

Cause: minimum_confidence isn't a number from 0.0 to 1.0. An empty value counts as invalid.

How to fix: Send a number such as 0.5, which is the default, or leave the field out.

Unable to calcuate audio duration.​

Status: 500 Internal Server Error. The message is spelled this way.

Cause: The transcriber didn't report how long the audio is, so Edge can't return or bill a result. A file with no audio can cause this. Nothing is billed.

How to fix: Check that the file contains audio, and send it again. If a valid file keeps failing, email support@speechischeap.com.

Something went wrong​

Status: 500 Internal Server Error

Cause: This message is generic. An unexpected error occurred inside Edge.

How to fix: Retry after a few seconds. If the same request keeps failing, email support@speechischeap.com with the time of the request and its time zone.

404 Not Found​

Status: 404 Not Found

Cause: This message is generic. Edge has three endpoints: POST /v2/jobs/, GET /v2/jobs/health, and GET /v2/jobs/auth. Any other path or method returns 404. Edge has no endpoint to read a job later. It returns the result in the response to the POST.

How to fix: Use one of the three endpoints, with the trailing slash on POST /v2/jobs/. See Upload a File.

Failed to decode audio file​

Status: 200 OK, with "status": "FAILED" and the message in output.error

The message starts with this text and may continue with more detail.

Cause: The transcriber couldn't decode the file. It isn't audio or video, it's corrupt, or its format isn't readable.

How to fix: Check that the file plays. Convert it to a supported type, such as MP3, and send it again. If a valid file keeps failing, email support@speechischeap.com.

"status": "FAILED"​

Status: 200 OK

Cause: Edge answers 200 with "status": "FAILED" for any failure while it transcribes, so check status as well as the HTTP code. output.error holds the reason. It is the transcriber's error message, cut to the part after the first : , which drops a leading error code. If the failure has no message, it's empty or reads Transcription failed. A FAILED request isn't billed.

How to fix: Read output.error and fix what it names. Retry once. If it keeps failing, email support@speechischeap.com with the time of the request and its time zone.

Failures After a Job Is Accepted​

The APIs accept a request before they look at the audio. A job can still fail afterward, for example when the file is too short or can't be decoded. The request already returned its 202, so the failure reaches you another way:

  • Jobs API and Upload: The job's status becomes FAILED. The reason arrives only in the webhook_url callback, as {"id": "…", "output": {"error": "…"}, "status": "FAILED"}. Get a Job shows FAILED with an empty output. Set a webhook_url if you need the reason. See Webhooks.
  • x402: The reason is in output.error of the webhook and of Get a Job. A duration failure can add output.duration_error, with the limits and the measured length. A purchase that ends before a job exists has a purchase_id instead of an id. Replay the paid request to read it.

A failed job isn't billed, and an x402 payment is refunded in full.

Expected at least 6 seconds; got <n> seconds​

Status: The request returned 202. The job then fails.

Cause: The audio is shorter than 6 seconds. <n> is its measured length.

How to fix: Transcribe audio that is at least 6 seconds long, for example by joining short clips. See Input Requirements.

Expected at most <max> seconds; got at least <n> seconds​

Status: The request returned 202. The job then fails.

Cause: The audio is longer than the limit. <max> is 86400 (24 hours) for the Jobs API, so the message reads Expected at most 86400 seconds; got at least <n> seconds. An x402 job has a lower limit: the duration your payment covers, such as 300. <n> is the length when measuring stopped, so the file is at least that long. For x402, output.duration_error shows the limit as max_input_duration and the measured length as actual_input_duration. actual_duration_is_lower_bound is true when measuring stopped early.

How to fix: Split the audio into parts under 24 hours. For x402, request a quote with a larger input_duration.

The input language ("<code>") does not appear in the list of supported languages: …​

Status: The request returned 202. The job then fails.

Cause: language isn't a code from Supported Languages. The message ends with the list of supported codes.

How to fix: Use a lowercase code from that page, or leave language empty to detect the language.

'minimum_confidence' must be between 0.0 and 1.0.​

Status: The request returned 202. The job then fails.

Cause: minimum_confidence isn't a number from 0 to 1. The Jobs API doesn't check it when you create the job. The x402 API rejects it earlier, with minimum_confidence must be a number from 0 to 1.

How to fix: Send a number from 0 to 1, such as 0.5, which is the default. See Create a Job from a URL.

FFmpeg process returned non-zero exit code <n>.​

Status: The request returned 202. The job then fails.

Cause: The audio couldn't be decoded. The message can continue with details from the decoder. Likely causes:

  • The file isn't audio or video, or it's corrupt or cut short.
  • The file's format isn't supported. See Supported File Types.
  • The URL stopped working after the job was accepted, for example an expired pre-signed URL or a server error. An uploaded file is deleted after about an hour.

How to fix: Check that the file plays on your computer. Convert it to a supported format if needed. Keep the URL valid until the job completes. If a valid file keeps failing, email support@speechischeap.com with the job ID.

Input media could not be reached​

Status: The paid request returns 202 with "status": "FAILED", a purchase_id, and no id. The webhook gets the same result.

Cause: An x402 URL job failed the reachability check after the service accepted your payment. No job was created, and the payment is refunded in full.

How to fix: Make the URL reachable without a login. Then request a new quote and pay again.

Upload failed​

Status: The purchase ends with "status": "FAILED", a purchase_id, and no id. Replay the paid request or wait for the webhook to read it.

Cause: An x402 upload didn't finish or was rejected. The payment is refunded in full. The reply from the upload service says why. See Upload.

How to fix: Request a new quote, pay, and upload again.

Paid purchase was not used before its deadline​

Status: The purchase ends with "status": "EXPIRED", a purchase_id, and no id. Replay the paid request or wait for the webhook to read it.

Cause: You paid for an upload but didn't start it within 300 seconds of the payment. The payment is refunded in full.

How to fix: Request a new quote, pay, and start the upload right away with the upload_token from the paid response.

Inference returned an invalid or over-limit input duration​

Status: The request returned 202. The job then fails.

Cause: The transcription finished, but the measured length is outside what your payment covers. output.duration_error shows the limits and the measured length. The payment is refunded in full.

How to fix: Request a quote with a larger input_duration and pay again.

Transcription failed​

Status: The request returned 202. The job then fails.

Cause: An x402 job failed and the transcription service gave no reason. The payment is refunded in full.

How to fix: Request a new quote and try again. If it keeps failing, email support@speechischeap.com with the job ID.

Transcription canceled​

Status: The job ends with "status": "CANCELED".

Cause: An x402 job was canceled. This is the result of a cancellation, not a failure. The payment is refunded in full.

How to fix: Nothing to fix. Request a new quote to run the job again.