Search Openverse for openly licensed sound effects, music and images that are safe for commercial use
One address for any MCP client, signed in with your Odel account.
4 read1 act
Grouped by the server's hints and tool names.
Odel charges nothing per call.
querystringrequiredWhat to search for
kindstringoptionalsound_effect (short Freesound clips), music (tracks, mostly Jamendo) or any
One of sound_effect, music, any
Default "sound_effect"
licencestringoptionalpublic-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)
One of public-domain, attribution
Default "public-domain"
maxSecondsnumberoptionalOnly return clips at most this long
pageintegeroptionalResult page, from 1
Default 1
pageSizeintegeroptionalResults per page, 1–20
Default 10
successbooleanrequiredOne of true
resultsobject[]requiredidstringrequiredtitlestring | nullrequiredcreatorstring | nullrequiredurlstringrequiredThe file itself, on the source’s own host
fileTypestring | nullrequiredlicensestringrequiredcc0, pdm or by
licenseVersionstring | nullrequiredlicenseUrlstring | nullrequiredattributionstring | nullrequiredThe credit line to publish with the work; null when none is required
landingUrlstring | nullrequiredThe work’s page at its source — check the licence there before use
sourcestring | nullrequiredtagsstring[]requireddurationSecondsnumber | nullrequiredresultCountintegerrequiredpageCountintegerrequiredpageintegerrequirednextPageinteger | nullrequiredPass as `page` to continue; null when there is nothing more
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "What to search for"
},
"kind": {
"type": "string",
"enum": [
"sound_effect",
"music",
"any"
],
"default": "sound_effect",
"description": "sound_effect (short Freesound clips), music (tracks, mostly Jamendo) or any"
},
"licence": {
"type": "string",
"enum": [
"public-domain",
"attribution"
],
"default": "public-domain",
"description": "public-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)"
},
"maxSeconds": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 3600,
"description": "Only return clips at most this long"
},
"page": {
"type": "integer",
"minimum": 1,
"default": 1,
"description": "Result page, from 1"
},
"pageSize": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"default": 10,
"description": "Results per page, 1–20"
}
},
"required": [
"query"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}querystringrequiredWhat to search for
licencestringoptionalpublic-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)
One of public-domain, attribution
Default "public-domain"
categorystringoptionalKind of image
One of photograph, illustration, digitized_artwork
aspectRatiostringoptionalImage shape
One of tall, wide, square
pageintegeroptionalResult page, from 1
Default 1
pageSizeintegeroptionalResults per page, 1–20
Default 10
successbooleanrequiredOne of true
resultsobject[]requiredidstringrequiredtitlestring | nullrequiredcreatorstring | nullrequiredurlstringrequiredThe file itself, on the source’s own host
fileTypestring | nullrequiredlicensestringrequiredcc0, pdm or by
licenseVersionstring | nullrequiredlicenseUrlstring | nullrequiredattributionstring | nullrequiredThe credit line to publish with the work; null when none is required
landingUrlstring | nullrequiredThe work’s page at its source — check the licence there before use
sourcestring | nullrequiredtagsstring[]requiredthumbnailUrlstring | nullrequiredwidthnumber | nullrequiredheightnumber | nullrequiredresultCountintegerrequiredpageCountintegerrequiredpageintegerrequirednextPageinteger | nullrequiredPass as `page` to continue; null when there is nothing more
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "What to search for"
},
"licence": {
"type": "string",
"enum": [
"public-domain",
"attribution"
],
"default": "public-domain",
"description": "public-domain (CC0 / public domain mark, no credit needed) or attribution (adds CC BY; use the returned `attribution` line)"
},
"category": {
"type": "string",
"enum": [
"photograph",
"illustration",
"digitized_artwork"
],
"description": "Kind of image"
},
"aspectRatio": {
"type": "string",
"enum": [
"tall",
"wide",
"square"
],
"description": "Image shape"
},
"page": {
"type": "integer",
"minimum": 1,
"default": 1,
"description": "Result page, from 1"
},
"pageSize": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"default": 10,
"description": "Results per page, 1–20"
}
},
"required": [
"query"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}typestringrequiredWhether the id is an audio or an image result
One of audio, image
idstring · uuidrequiredThe Openverse id from a search result
relatedbooleanoptionalAlso return up to 10 similar works
Default false
successbooleanrequiredOne of true
typestringrequiredOne of audio, image
itemanyrequiredrelatedany[]optional{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"audio",
"image"
],
"description": "Whether the id is an audio or an image result"
},
"id": {
"type": "string",
"format": "uuid",
"description": "The Openverse id from a search result"
},
"related": {
"type": "boolean",
"default": false,
"description": "Also return up to 10 similar works"
}
},
"required": [
"type",
"id"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}Takes no parameters.
successbooleanrequiredOne of true
keySourcestringrequiredWhose Openverse key your searches use: your own, Odel’s shared one, or none (anonymous)
One of yours, odel, anonymous
verifiedboolean | nullrequiredWhether that key’s email address is verified (unverified keys get anonymous rate limits); null when anonymous
rateLimitModelstring | nullrequiredstandard or enhanced; null when anonymous
requestsThisMinuteinteger | nullrequiredrequestsTodayinteger | nullrequired{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}emailstring · emailrequiredWhere Openverse sends the verification link; must be the user’s own address
namestringoptionalA unique name for the client; a random one is picked when omitted
descriptionstringoptionalWhat the client is for; Openverse asks for detail
successbooleanrequiredOne of true
clientIdstringrequiredclientSecretstringrequiredShown once — Openverse cannot show it again
namestringrequirednextStepsstring[]required{
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"maxLength": 254,
"description": "Where Openverse sends the verification link; must be the user’s own address"
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 150,
"description": "A unique name for the client; a random one is picked when omitted"
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 10000,
"description": "What the client is for; Openverse asks for detail"
}
},
"required": [
"email"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}@Odel/openverse An Odel module that searches Openverse for openly licensed sound effects, music and images, filtered to licences that are safe for commercial use. Built on @odel/module-sdk.
Made using Openverse. Not endorsed by Openverse. Licences are as reported by the source and are not verified by Openverse or Odel — check a result's landingUrl before you use it.
Tools Every tool works without setup: searches run on Odel's shared Openverse key unless you add your own (see Which key a search uses).
Tool Inputs Returns search-audio query · kind: sound_effect (default) | music | any · licence: public-domain (default) | attribution · maxSeconds? · page · pageSize (1–20) results[], resultCount, pageCount, page, nextPage search-images query · licence · category? (photograph | illustration | digitized_artwork) · aspectRatio? (tall | wide | square) · page · pageSize the same get-media type: audio | image · id · related (default false) item, and related[] (up to 10) when asked register-client email · name? (unique; random when omitted) · description? clientId, clientSecret (shown once), name, nextSteps[] client-status — keySource (yours | odel | anonymous), verified, rateLimitModel, requestsThisMinute, requestsToday Every item carries url (the file, on the source's own host), license, licenseUrl, attribution (the credit line to publish, or null when none is needed) and landingUrl. Audio items add durationSeconds; image items add thumbnailUrl, width and height.
Licences. public-domain returns only CC0 and the public domain mark — nothing to credit. attribution adds CC BY, with its credit line in attribution. Share-alike, no-derivatives and non-commercial works are never returned, and results are re-checked after the fetch in case the upstream filter changes. get-media serves the attribution set. Most music on Openverse (Jamendo) is CC BY, so music searches usually want licence: "attribution".
maxSeconds is applied after the fetch, because Openverse only filters by coarse length buckets; a call reads at most 3 upstream pages to fill pageSize. Pass nextPage back as page to continue.
Errors come back as an isError result whose text is { success: false, error, code, metadata? }, with code from the SDK's ErrorCode: 2001 only one of your two secrets set, 2002 your own credentials rejected by Openverse, 1001 bad input, 3004 not found, 4001 rate limited (metadata.retryAfter; on the shared key or anonymous use, metadata.keySource and a pointer to register-client), 3001 Openverse unavailable, 3002 network, 3003 timeout.
Which key a search uses Your own Openverse client, when both of your secrets are set: an allowance to yourself. Odel's shared key otherwise: a verified client Odel registered for this module and holds as Worker secrets. Every Odel user without their own key shares its allowance. Anonymous, when neither is available (a local run without Odel's key, say). Caller Requests a minute Requests a day Results per page A verified client (your own, or Odel's shared one) 100 10,000 up to 20 here An unverified client, or anonymous (per IP) 20 200 20 These are Openverse's published defaults, read from its rate-limit headers; Openverse can raise a client's limits on request.
Secret (optional) What OPENVERSE_CLIENT_ID Your Openverse client id OPENVERSE_CLIENT_SECRET Your Openverse client secret (shown only once) Setting only one of the two is answered 2001, naming the missing one: add it, or remove both to go back to Odel's key.
Getting your own key. Worth it if you keep hitting the shared limit (a 4001 that points at register-client). Ask your assistant to run register-client with your own email address; it needs no account. It returns the client id and secret (the secret is shown once), so store both, say in your Odel vault. Click the link Openverse emails you before adding them: until you verify, Openverse treats the client as anonymous, which is less than the shared key. Then add both on the module's Config tab (how) and run client-status to confirm keySource: "yours" and verified: true.
Registering by hand.
curl -s -X POST https://api.openverse.org/v1/auth_tokens/register/
-H 'Content-Type: application/json'
-d '{"name": "my-odel-openverse", "description": "Searching Openverse from Odel", "email": "you@example.com"}'
Save client_id and client_secret from the response — the secret can't be retrieved again — and click the link Openverse emails you. Until you do, the client stays on anonymous rate limits.
Development pnpm install pnpm test # vitest, Openverse replaced by a stubbed fetch pnpm typecheck pnpm dev # wrangler dev; send your own key in params._meta["app.odel/secrets"] Worker bindings:
ODEL_OPENVERSE_CLIENT_ID / ODEL_OPENVERSE_CLIENT_SECRET (secrets): Odel's shared key. Locally, put them in .dev.vars (gitignored); without them local searches go anonymous. OPENVERSE_API_BASE (a variable, default https://api.openverse.org/v1) points the module at another Openverse, such as a local stub. License MIT