# Stenobird API

Audio transcripts from public URLs.

## Pricing

- API-key accounts are billed weekly at $0.01 per completed audio minute.
- Billable minutes are rounded down to whole audio minutes, with a 1 minute minimum per completed job.

## Create transcript

```http
POST /v1/transcripts
Content-Type: application/json
```

```json
{
  "audio_url": "https://example.com/audio-url"
}
```

Behavior:

- Without an API key, Stenobird streams a free 15 second preview.
- With `Authorization: Bearer <api_key>`, Stenobird streams the full transcript and meters completed audio minutes.

Response:

```json
{
  "id": "tr_123",
  "status": "queued",
  "mode": "preview",
  "status_url": "https://stenobird.com/v1/transcripts/tr_123",
  "poll_url": "https://stenobird.com/v1/transcripts/tr_123",
  "events_url": "https://stenobird.com/v1/transcripts/tr_123/events",
  "result_url": "https://stenobird.com/v1/transcripts/tr_123/result",
  "terminal": false,
  "result_ready": false,
  "retry_after_seconds": 2
}
```

## Stream events

```http
GET /v1/transcripts/{id}/events
```

Example events:

```text
event: status
data: {"id":"tr_123","status":"queued","stage":"validating_url","terminal":false,"result_ready":false,"status_url":"https://stenobird.com/v1/transcripts/tr_123","events_url":"https://stenobird.com/v1/transcripts/tr_123/events","result_url":"https://stenobird.com/v1/transcripts/tr_123/result","retry_after_seconds":2}

event: status
data: {"id":"tr_123","status":"transcribing","stage":"submitted","terminal":false,"result_ready":false,"status_url":"https://stenobird.com/v1/transcripts/tr_123","events_url":"https://stenobird.com/v1/transcripts/tr_123/events","result_url":"https://stenobird.com/v1/transcripts/tr_123/result","retry_after_seconds":2}

event: transcript
data: {"id":"tr_123","status":"transcribing","stage":"transcribing","text":"Hello and welcome to...","chunk_index":0}

event: transcript
data: {"id":"tr_123","status":"transcribing","stage":"transcribing","text":" today we are talking about...","chunk_index":1}

event: preview_locked
data: {"id":"tr_123","status":"preview_complete","terminal":true,"result_ready":true,"message":"Preview ended. Add an API key to get the full transcript.","upgrade_url":"https://stenobird.com/pricing"}

event: done
data: {"id":"tr_123","status":"preview_complete","terminal":true,"result_ready":true,"result_url":"https://stenobird.com/v1/transcripts/tr_123/result"}
```

Full transcript requests use the same endpoints. Add `Authorization: Bearer <api_key>` on `POST /v1/transcripts`, `GET /v1/transcripts/{id}`, `GET /v1/transcripts/{id}/events`, and `GET /v1/transcripts/{id}/result`.

Client flow:

1. Create the transcript with `POST /v1/transcripts`.
2. Read `events_url` until you receive `event: done` with `"terminal": true`.
3. If `"result_ready": true`, fetch `result_url`.
4. If `GET result_url` returns `409 transcript_not_ready`, wait `retry_after_seconds` and try again.
5. If `POST /v1/transcripts` returns `429 queue_busy`, wait `retry_after_seconds` and retry job creation.

## Agent skill

Install the Stenobird skill:

```bash
curl -fsSL https://stenobird.com/skills/stenobird-transcribe/install.sh | sh
```

Compatible agents: Codex, Claude Code, Pi, Hermes, OpenClaw.

Example prompt:

```text
Codex: Use $stenobird-transcribe with this audio:
https://www.dwarkesh.com/p/jensen-huang

Claude Code, Hermes, OpenClaw: /stenobird-transcribe https://www.dwarkesh.com/p/jensen-huang
Pi: /skill:stenobird-transcribe https://www.dwarkesh.com/p/jensen-huang
```

The skill sends the audio URL to Stenobird, streams transcript events, and uses `STENOBIRD_API_KEY` when present for full transcripts.

## Create API key account

```http
POST /v1/billing/checkout
Content-Type: application/json
```

```json
{
  "email": "you@example.com"
}
```

Send the user to `/pricing`. After checkout returns to `/success?checkout_session_id=...`, Stenobird verifies checkout and shows the API key once.

## Supported input

Supported:

- any public URL with audio behind it
- any direct public audio media URL

Not supported:

- private pages or audio requiring login
- pages with no discoverable podcast feed, audio player, or supported directory metadata

## Finding audio

Stenobird looks for audio at the submitted URL before starting transcription.

If Stenobird cannot find the audio automatically, send the direct audio URL.

Example RSS item:

```xml
<item>
  <title>Episode title</title>
  <enclosure
    url="https://cdn.example.com/show/episode.mp3"
    type="audio/mpeg"
    length="123456789" />
</item>
```

## Error response

```json
{
  "error": "unsupported_url",
  "message": "The URL did not resolve to public audio Stenobird can transcribe.",
  "accepted_url_examples": [
    "https://example.com/audio-url",
    "https://media.example.com/path/to/audio.mp3"
  ],
  "unsupported_url_examples": [
    "https://example.com/article-without-audio"
  ]
}
```

Documented error codes:

- invalid_url
- unsupported_url
- audio_unreachable
- audio_source_not_found
- audio_source_unreachable
- directory_lookup_failed
- extractor_unavailable
- audio_content_type_not_supported
- audio_too_large
- transcription_failed
- billing_failed
- transcript_not_ready
- queue_busy
- webhook_requires_api_key
- rate_limited

## Payment modes

Use `Authorization: Bearer <api_key>` for full transcripts. Account usage is metered after successful completion and charged weekly.

## OpenAPI

Schema: `/openapi.json`.