|
| 1 | +# audd |
| 2 | + |
| 3 | +[](https://github.com/AudDMusic/audd-node/actions/workflows/ci.yml) |
| 4 | +[](https://github.com/AudDMusic/audd-node/actions/workflows/contract.yml) |
| 5 | +[](https://www.npmjs.com/package/audd) |
| 6 | + |
| 7 | +Official TypeScript / Node.js SDK for the [AudD](https://audd.io) music recognition API. |
| 8 | + |
| 9 | +## Quickstart |
| 10 | + |
| 11 | +```bash |
| 12 | +npm install audd |
| 13 | +``` |
| 14 | + |
| 15 | +```ts |
| 16 | +import { AudD } from "audd"; |
| 17 | + |
| 18 | +const audd = new AudD({ apiToken: "test" }); // grab a real token at https://dashboard.audd.io |
| 19 | +const result = await audd.recognize("https://audd.tech/example.mp3"); |
| 20 | +if (result) { |
| 21 | + console.log(`${result.artist} — ${result.title}`); |
| 22 | +} |
| 23 | +``` |
| 24 | + |
| 25 | +## Capabilities |
| 26 | + |
| 27 | +| What | How | |
| 28 | +|---|---| |
| 29 | +| Recognize a short clip (≤25s) | `audd.recognize(source)` | |
| 30 | +| Recognize a long file (hours, days) | `audd.recognizeEnterprise(source, { limit: ... })` | |
| 31 | +| Manage real-time stream recognition | `audd.streams.add({ url, radioId })` etc. | |
| 32 | + |
| 33 | +`source` accepts a URL string, a `URL` object, a file path (Node only), |
| 34 | +a `Blob` / `File`, or a `Uint8Array` / `Buffer` — auto-detected. |
| 35 | + |
| 36 | +The full TypeScript types ship in the package — no `@types/audd` needed. |
| 37 | + |
| 38 | +## Errors |
| 39 | + |
| 40 | +Every server error becomes a typed exception: |
| 41 | + |
| 42 | +```ts |
| 43 | +import { AudD, AudDAuthenticationError, AudDSubscriptionError } from "audd"; |
| 44 | + |
| 45 | +try { |
| 46 | + await new AudD({ apiToken: "bad" }).recognize("https://x.mp3"); |
| 47 | +} catch (e) { |
| 48 | + if (e instanceof AudDAuthenticationError) { |
| 49 | + console.log(`check your token: ${e.errorCode} ${e.serverMessage}`); |
| 50 | + } else if (e instanceof AudDSubscriptionError) { |
| 51 | + console.log("this endpoint isn't enabled on your token"); |
| 52 | + } else { |
| 53 | + throw e; |
| 54 | + } |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +The full hierarchy is in [`src/errors.ts`](src/errors.ts). Every |
| 59 | +`AudDAPIError` carries `errorCode`, `serverMessage`, `httpStatus`, |
| 60 | +`requestId`, `requestedParams`, `requestMethod`, `brandedMessage`, and |
| 61 | +`rawResponse`. |
| 62 | + |
| 63 | +## Forward compatibility |
| 64 | + |
| 65 | +Models accept and round-trip unknown server fields via `extras`: |
| 66 | + |
| 67 | +```ts |
| 68 | +const result = await audd.recognize("https://example.mp3", { return: ["apple_music"] }); |
| 69 | +console.log(result?.appleMusic?.url); // typed |
| 70 | +console.log(result?.extras); // any unknown server fields |
| 71 | +console.log(result?.rawResponse); // full unparsed JSON object |
| 72 | +``` |
| 73 | + |
| 74 | +If AudD adds a new metadata block tomorrow (e.g., `tidal`), you can read |
| 75 | +it as `result.extras.tidal` *today* — no SDK release needed. The next SDK |
| 76 | +release adds the typed `tidal` field, and both paths keep working. |
| 77 | + |
| 78 | +## Configuration |
| 79 | + |
| 80 | +```ts |
| 81 | +import { AudD } from "audd"; |
| 82 | + |
| 83 | +const audd = new AudD({ |
| 84 | + apiToken: "...", |
| 85 | + maxRetries: 3, // retry budget per call |
| 86 | + backoffFactorMs: 500, // initial backoff (ms), jittered, exponential |
| 87 | + fetch: customFetch, // bring your own fetch (proxy, mTLS, observability) |
| 88 | +}); |
| 89 | +``` |
| 90 | + |
| 91 | +A single `AudD` instance is safe to share across concurrent requests; |
| 92 | +`setApiToken(...)` rotates the token without aborting in-flight calls. |
| 93 | + |
| 94 | +Default timeouts: 60s for standard endpoints, **1 hour** for the |
| 95 | +enterprise endpoint. Pass `timeoutMs` per call to override. |
| 96 | + |
| 97 | +## Streams |
| 98 | + |
| 99 | +Manage real-time stream recognition and consume events: |
| 100 | + |
| 101 | +```ts |
| 102 | +await audd.streams.add({ url: "https://stream.example/live.m3u8", radioId: "my-radio" }); |
| 103 | + |
| 104 | +for await (const event of audd.streams.longpoll("my-radio")) { |
| 105 | + console.log(event); |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +### Tokenless longpoll (browser / widget) |
| 110 | + |
| 111 | +For browser or widget builds where you can't ship the api_token, the |
| 112 | +`LongpollConsumer` is exported from a separate sub-entry so bundlers |
| 113 | +tree-shake the auth client out: |
| 114 | + |
| 115 | +```ts |
| 116 | +import { LongpollConsumer } from "audd/longpoll"; |
| 117 | + |
| 118 | +// `category` is derived server-side via |
| 119 | +// audd.streams.deriveLongpollCategory(radioId), then shipped to the |
| 120 | +// browser. The consumer carries no api_token. |
| 121 | +const consumer = new LongpollConsumer("abc123def"); |
| 122 | +for await (const event of consumer.iterate({ timeout: 30 })) { |
| 123 | + console.log(event); |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +## Custom catalog (advanced — not for music recognition) |
| 128 | + |
| 129 | +> ⚠ **The custom-catalog endpoint is NOT how you submit audio for music |
| 130 | +> recognition.** For recognition, use `recognize()` or |
| 131 | +> `recognizeEnterprise()`. The custom-catalog endpoint adds songs to your |
| 132 | +> private fingerprint database for *your* account. Requires special |
| 133 | +> access — contact api@audd.io if you need it. |
| 134 | +
|
| 135 | +```ts |
| 136 | +await audd.customCatalog.add({ |
| 137 | + audioId: 42, |
| 138 | + source: "https://my.song.mp3", |
| 139 | +}); |
| 140 | +``` |
| 141 | + |
| 142 | +## Advanced |
| 143 | + |
| 144 | +A generic raw-request escape hatch lets you call newly-shipped server |
| 145 | +methods before the SDK has a typed wrapper: |
| 146 | + |
| 147 | +```ts |
| 148 | +const raw = await audd.advanced.rawRequest("someNewMethod", { q: "x" }); |
| 149 | +``` |
| 150 | + |
| 151 | +## Resource cleanup |
| 152 | + |
| 153 | +Both `AudD` and `LongpollConsumer` support |
| 154 | +[`Symbol.asyncDispose`](https://github.com/tc39/proposal-explicit-resource-management) |
| 155 | +where the runtime supports it. For older runtimes, call `close()` |
| 156 | +manually: |
| 157 | + |
| 158 | +```ts |
| 159 | +{ |
| 160 | + await using audd = new AudD({ apiToken: "..." }); |
| 161 | + await audd.recognize("..."); |
| 162 | +} // close() called automatically here |
| 163 | +``` |
| 164 | + |
| 165 | +## Spec contract |
| 166 | + |
| 167 | +This SDK builds against the |
| 168 | +[`audd-openapi`](https://github.com/AudDMusic/audd-openapi) spec. The |
| 169 | +contract tests in `test/contract.test.ts` validate the parsers against |
| 170 | +the canonical fixture set on every push, on a daily cron, and on every |
| 171 | +spec update. |
| 172 | + |
| 173 | +## License |
| 174 | + |
| 175 | +MIT — see [LICENSE](./LICENSE). |
| 176 | + |
| 177 | +## Support |
| 178 | + |
| 179 | +- Documentation: https://docs.audd.io |
| 180 | +- Tokens: https://dashboard.audd.io |
| 181 | +- Email: api@audd.io |
0 commit comments