Last updated: October 8, 2026
The Dropline API lets your own apps, scripts and Shortcuts dictate with a person's Dropline: their dictionary, their memory, and the same writing Dropline does on the Mac and the iPhone. It can also give Dropline text to learn from, so the names and terms in it come out right the next time that person dictates, anywhere.
The API is in preview. It is free while it is, and it may still change.
1. Get started
- Sign in at dropline.fish.audio/app with your Fish Audio account and create a key under API keys. The key is shown once, so copy it then.
- Send the key as a bearer token with every request.
export DROPLINE_KEY=dl_...
curl https://api.dropline.fish.audio/v1/me \
-H "Authorization: Bearer $DROPLINE_KEY"
Then dictate a recording. Any common audio format works.
curl https://api.dropline.fish.audio/v1/dictate \
-H "Authorization: Bearer $DROPLINE_KEY" \
-F audio=@recording.m4a
{
"request_id": "dct_8f3c...",
"mode": "dictate",
"final_text": "Let's meet at 4 tomorrow in the third-floor room.",
"transcript": {"text": "lets meet at four tomorrow in the third floor room", "language": "en"},
"rewrite": {"status": "used", "reason": null},
"timings_ms": {"total": 1340}
}
final_text is what to insert.
2. Authentication
Every request carries Authorization: Bearer <token>. The token is a Dropline key or an OAuth access token. Both act as one Dropline account, and both can call the /v1 routes only.
2.1 Dropline keys
- For your own scripts, Shortcuts and tools. A key starts with
dl_and acts as the account that created it. - Create and delete keys at dropline.fish.audio/app. An account can have up to 20. The panel shows when each key was last used.
- A deleted key stops working within a minute.
- Anyone who has a key can use it as you. Keep it out of code you share.
2.2 OAuth, for apps other people use
If other people use your app, let each of them connect their own Dropline with OAuth 2.1 instead of asking for a key. Dropline signs people in with their Fish Audio account.
- Register your app once, with dynamic client registration. Only public clients are registered, and no client secret is issued: use PKCE, also on a server.
curl https://api.fish.audio/oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name": "My Notes", "redirect_uris": ["https://notes.example.com/callback"]}'
- Send the person to
https://fish.audio/oauth/authorizewithresponse_type=code, yourclient_idandredirect_uri,scope=dropline:api,resource=https://api.dropline.fish.audio, astate, and a PKCEcode_challengewithcode_challenge_method=S256. They sign in, choose a team and agree. The consent page shows yourclient_name. - Exchange the code at
https://api.fish.audio/oauth/token: a form post withgrant_type=authorization_code,code,client_id,redirect_uriandcode_verifier. - Access tokens last an hour. Refresh tokens last 60 days, and every refresh (
grant_type=refresh_token) returns a new pair. - Redirect URIs must be https, or http on localhost for native apps (any port).
- Metadata:
https://api.fish.audio/.well-known/oauth-authorization-serverfor the authorization server, andhttps://api.dropline.fish.audio/.well-known/oauth-protected-resourcefor the API.
Dictations from your app appear under your client_name in Dropline, and text it sends to /v1/context is labelled with it.
3. Conventions
- Base address:
https://api.dropline.fish.audio. JSON in and out, except the multipart dictation request. - Request IDs: every response has a
request_id, also in theX-Request-Idheader. Quote it when you report a problem. - Errors look like this, with the HTTP status matching the code:
{"error": {"code": "rate_limited", "message": "too many requests", "retryable": true}, "request_id": "dct_..."}
401 unauthorized: no token, or one that is unknown, deleted or expired. Refresh an OAuth token once, then ask the person to connect again.403 insufficient_scope: the token was not granteddropline:api.413 audio_too_longorpayload_too_large: over the limits in section 7.422 audio_invalid: the audio could not be read.422 bad_request: the request is malformed.429 rate_limited: wait for the seconds inRetry-After.502 stt_failed,503 upstream_unavailable,504 stt_timeout: speech recognition failed this time. Retry once.503 dictionary_unavailable: the dictionary could not be read or written. Retry in a moment.
4. Dictation
4.1 POST /v1/dictate
Speech in, finished text out: Dropline recognizes the recording with the person's dictionary and memory and writes it as they would type it, with punctuation, formatting and their spelling of names.
The request is multipart/form-data with these parts, in this order:
request(optional, JSON): settings for this dictation, described below.context(optional): what is around the cursor, which helps Dropline pick the right words. Plain text, or the JSON object in section 4.2.audio(required): the recording, at most 180 seconds and 8 MB. WAV, M4A/AAC, MP3, Ogg or WebM with Opus, and FLAC all work.
Every field of request is optional:
mode:dictate(the default) oredit, see section 4.3.language:auto(the default) works for every language Dropline writes;zhorenfixes the language.locale: the person's locale, such asja-JP, when it is known. It helps when the language is unclear.punctuation:fullwidth(the default) orhalfwidth, for Chinese and Japanese text.cjk_spacing:true(the default) puts spaces between Chinese and Latin letters or digits.single_line:truewhen the text goes into a one-line field, such as a terminal prompt.
curl https://api.dropline.fish.audio/v1/dictate \
-H "Authorization: Bearer $DROPLINE_KEY" \
-F 'request={"language": "auto", "punctuation": "halfwidth"};type=application/json' \
-F 'context={"app": "Notion", "title": "Q4 plan", "before": "Agenda for Friday:"};type=application/json' \
-F audio=@recording.m4a
The response:
final_text: the text to insert. In edit mode, the text that replaces the selection.transcript:textandlanguageof what was heard, before Dropline wrote it up. When the dictation was written in one step,textis that writing andlanguageisnull.rewrite.status:usedwhenfinal_textis Dropline's writing.skipped,rejectedorfailedmeanfinal_textis the transcript.timings_ms: how long it took, in milliseconds.
Dictations go into the person's memory, like dictations from the Dropline apps.
4.2 Context
The context part tells Dropline where the text goes. Send whatever you know; every field is optional.
{
"app": "Notion",
"title": "Q4 plan",
"url": "https://www.notion.so/...",
"before": "the text before the cursor",
"after": "the text after the cursor",
"selected": "the selected text",
"text": "other text on the screen or in the document"
}
A plain-text part counts as text. Long fields are cut to what Dropline uses. Context is used for this one dictation: to keep something for later, send it to /v1/context (section 5.1).
4.3 Editing by voice
With "mode": "edit", the person says how to change the text in selected, such as "make this more polite" or "translate to English", and final_text is the changed text.
- The response also has
edit, withoutcome(replaced,written,deleted,unchanged,not_edit,needs_selection,failed, or one of therefused_outcomes) anddelivery(replace_selection,insert_at_cursor,delete_selection,clipboardornone). - Only
replacedandwrittencome with text. Edits are not remembered.
5. Teaching Dropline
5.1 POST /v1/context
Give Dropline text the person is working with, such as a note, a document, a meeting agenda or a chat. Dropline learns the names and terms in it, so they are spelled right in later dictations, and remembers what it is about.
curl https://api.dropline.fish.audio/v1/context \
-H "Authorization: Bearer $DROPLINE_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Kickoff with Anika Reyes and the Zephyr team: move the beta to Nov 12.", "title": "Zephyr kickoff", "source": "Notes"}'
text(required): up to 20,000 characters.title(optional): up to 200 characters.source(optional): where it comes from, up to 64 characters. It defaults to your app's or key's name.
The answer is 202 Accepted with {"memory": true, "request_id": "ctx_..."}. The work happens after the answer, usually within a few seconds:
- Dictionary: the names and terms that recognition would otherwise get wrong are added to the person's dictionary. Words it already writes correctly are left out, however rare. Words the person deleted are not added again.
- Memory: the text is remembered for about two weeks as something the person is working on, and helps Dropline understand later dictations. It is never treated as something they said.
The same text sent again within an hour is accepted and skipped.
5.2 POST /v1/terms
Suggest specific words for the dictionary, up to 300 at a time.
curl https://api.dropline.fish.audio/v1/terms \
-H "Authorization: Bearer $DROPLINE_KEY" \
-H "Content-Type: application/json" \
-d '{"terms": [{"term": "Zephyr", "example": "the Zephyr beta"}, {"term": "冷月", "pinyin": "leng yue"}]}'
Each term can have an example sentence, a count of how often it appears, and a pinyin for Chinese words. Dropline checks every term the way it checks words from context, and adds only those it would otherwise get wrong:
{"added": ["Zephyr"], "skipped": [{"term": "冷月", "reason": "review"}], "dictionary_version": 1791420000123, "request_id": "..."}
review means Dropline did not need the word. refused means the dictionary did not take it: the person deleted it before, or the dictionary is full.
6. Reading
6.1 GET /v1/me
The account the token acts as.
{"user": {"id": "...", "name": "Anika Reyes", "email": "anika@example.com", "avatar_url": "https://..."}, "team": {"id": "...", "name": "Personal"}, "memory": true}
6.2 GET /v1/vocabulary
The person's dictionary, for example to bias your own recognition or to spell names their way.
{"terms": [{"term": "Zephyr", "aliases": ["zefir"], "pinyin": null, "section": "manual"}], "version": 1791420000123}
section is manual for words the person added and auto for words Dropline added. aliases are what the term may be heard as.
7. Limits
- Audio: at most 180 seconds and 8 MB per dictation.
- Dictation: 30 a minute per account, shared with the person's Dropline apps.
- Context: 60 requests a minute per account, up to 20,000 characters each.
- Terms: 60 requests a minute per account, up to 300 terms each.
The limits may change during the preview. For more, or for anything else, write to us on Discord.
8. Your data
What you send through the API is handled like what the Dropline apps send, under the Dropline Privacy Policy and the Dropline Terms of Service. People can edit their dictionary, see their memory and clear it at dropline.fish.audio/app.