Let's say your app already shows the Verse of the Day as text. It works, but it looks like a text message. The Bible App shows that same verse as a piece of art, and your users have probably seen it there. The Images APIs let you show that same art in your own app: the daily verse image, images that match a search, and every image we have for a given passage.
There are two halves to the picture. The API tells you which image goes with a verse and hands you an opaque image_id or a full image record. The CDN serves the actual bytes at whatever size you ask for. You never build an image URL yourself; the API always gives it to you.
| Endpoint | Use it when you want… | Paginated |
|---|---|---|
GET /v1/verse-of-the-days/{day} | Today's verse with its image, in the user's language | No |
GET /v1/search-unified | A search box that returns verses, topics, and images in one call | No |
GET /v1/search-images | To page through every image that matches a search | Yes |
GET /v1/images | Every image for a specific verse or passage, or to browse a language's art | Yes |
GET /v1/images/{image_id} | To turn an image_id into a CDN URL, dimensions, and attribution | No |
Browse the full request and response schemas in the interactive API reference.
Beta channel. The Images APIs are in beta. During the beta they are served on the /v1-beta channel (for example https://api.youversion.com/v1-beta/images) for App Keys enrolled in the beta program, and the image_id field on Verse of the Day and the images kind on unified search appear only on that channel. A non-enrolled key gets 401 with oauth.v2.InvalidApiKeyForGivenResource. If you want in, reach out to support with your App Key.
When these APIs promote to stable, the paths lose the -beta suffix and nothing else changes. The examples below use $YVP_API_BASE so switching is a one-line edit.
Before You Start
- Create your developer account and register your application at platform.YouVersion.com to obtain an App Key.
- Every request must include your App Key in the
X-YVP-App-Keyheader. See Authentication for details. No user sign-in is needed; verse images are app-scoped, not user-scoped.
Code
Key Concepts
An image is a record, not a file
The API never returns image bytes. It returns an Image record that points at the CDN:
| Field | Description |
|---|---|
id | The image's stable id: the same value Verse of the Day returns as image_id and unified search returns as id. Store, cache, and deduplicate on this one. In /v1/images and search-images listings, each item's id is a per-response reference instead; see Image ids and references. |
image_url | The CDN URL for the art. It already carries your App Key as an app_key query parameter; keep it, because the CDN uses it to attribute image loads to your app. To pick a rendition, add &size=thumb, &size=card, or &size=full; leave it off for full size. See Picking a size. |
passage_id | The passage the image illustrates: a verse (JHN.3.16) or a verse range (1CO.13.4-8). Same format the passages API accepts, so you can hand it straight back to fetch the text. |
attribution | The credit line for the image, for example © YouVersion. |
language_tag | BCP 47 tag of the language the art's text is in, for example en or es. |
width, height | Native dimensions in pixels. |
aspect_ratio | width / height rounded to three places (1.0 for square art, 0.562 for 9:16 portrait). Use it to reserve layout space before the image loads. |
Two of the entry points, Verse of the Day and Search, return just the id (plus passage_id for search). One extra call to GET /v1/images/{image_id} turns any id into the full record above. The /v1/images collection skips that step for rendering and returns full records directly.
Image ids and references
Image ids come in two forms. Both are opaque: you can't construct or reverse either one, so always take them from the API and never hard-code one.
- Stable ids look like
cHJlcmVuZGVyZWQtdmVyc2UtaW1hZ2U6OTU2MzTSuDmYPvbDHD-9UateZkHP. Verse of the Day (image_id), unified search, and the record fromGET /v1/images/{image_id}(id) return them. Treat one like a claim ticket: it is safe to store, safe to put in a URL, and it keeps resolving to the same image. Don't expect an id from one environment to resolve in another. - References are what the
/v1/imagescollection andsearch-imagesreturn as each item'sid. A reference is a long token (about 500 characters, startingeyJ) minted for that one response, so the same image gets a different reference every time you list it. Use it soon after you receive it, and don't store it or compare it across responses. Pass it toGET /v1/images/{image_id}as is; the record that comes back carries the image's stableid.
A malformed or unknown id returns 404 Image not found, without telling you which of the two it was.
The text always comes back
No verse art exists for every passage in every language. Every image-bearing endpoint is built so the missing-image case is a normal 200, never an error:
- Verse of the Day returns the verse with
image_id: null. - The
/v1/imagescollection and both search endpoints return200with an empty list.
Design your UI with a text-only fallback and check for null or empty rather than catching errors.
Language selection
Verse art has words on it, so language matters. Verse of the Day and the /v1/images collection pick the art's language from language_ranges[], an ordered list of RFC 4647 Basic language ranges. Repeat the parameter to supply more than one, in preference order:
Code
Ranges are tried in the order you give them, and resolution picks a language, not a particular image: it stops at the first language it recognizes, even if that language has no art for the day or passage you asked about. A later range is used only when an earlier one names a language the art catalog doesn't know. So es&en gives you Spanish art when there is some and no image when there isn't; it doesn't drop to English for that one day or passage. If you'd rather show English art than none, make a second request with language_ranges[]=en when the first comes back empty.
The two endpoints differ in how they treat a regional range:
- Verse of the Day falls back through each range's own chain:
es-MXtries Mexican Spanish first, then Spanish. - The
/v1/imagescollection stops at the first tag the image library recognizes. Regional tags such ases-MX,pt-BR, anden-USare recognized but currently have no art, so they return an empty collection rather than falling back toes, and resolution stops there even if you listesnext. Strip the region before calling the collection (sendes, notes-MX). Chinese is the exception: its art is tagged by region only, so ask forzh-CN,zh-TW, orzh-HK; a barezhreturns nothing.
On Verse of the Day the wildcard * resolves to English; on the /v1/images collection it means "every language."
Search works differently:
GET /v1/search-imagestakes no language parameter. Itsbible_iddecides the language searched, exactly as it does for verse search.GET /v1/search-unifiedstill requireslanguage_ranges[], as it always has, but there each range must exactly match a language search supports. There is noes-MX→esfallback, and*isn't accepted. If none of your ranges match, the request returns400withNone of the requested language_ranges are supported by search., so end the list with a base tag such asenrather than relying on a regional one alone.
Picking a size
Every image_url accepts one optional size query parameter. The URL already has a query string (?app_key=...), so add size with &, not ?. A second ? doesn't fail: the CDN ignores the size and quietly sends the full original. A URL builder (URLSearchParams in JavaScript, URLComponents on iOS, Uri.Builder on Android) saves you from splicing the string by hand. The CDN resizes from a single cached original, keeps the aspect ratio, and never upscales, so a 1080-pixel-wide original stays 1080 wide even at card.
size | Width | Good for |
|---|---|---|
thumb | 320 px | Lists, grids, pickers |
card | 1200 px | Feed cards, detail views, most phone screens |
full | up to 2048 px | Sharing, wallpapers, hero images |
| (omitted) | same as full | Getting started |
Sizes are presets on purpose. You can't pass raw pixel dimensions, and an unknown value or a duplicated size parameter returns 400 with a plain-text body: Invalid verse image size. Valid: size=thumb|card|full (omit for full).
Requesting thumb for a grid and card for the detail view is the difference between a download of a few tens of kilobytes and one of several hundred per image. Your users on cellular will notice.
Attribution
Every Image record carries an attribution string. Show it next to the image wherever the image appears, the same way you show Bible version attribution next to verse text. Verse of the Day and Search hand you only an id, so resolve the id through GET /v1/images/{image_id} before you render; the attribution comes back with the URL, and you never have to look it up separately.
If you'd also like to show the Bible App mark on your verse art, see the optional branding overlay. It's a nice touch, not a requirement, and it never replaces attribution.
Caching
Verse of the Day responses and Image records from GET /v1/images/{image_id} carry Cache-Control: public, max-age=86400, so cache them for the day. Search responses take their lifetime from the Bible you searched, so a licensed bible_id can come back with a shorter one. Pages from the /v1/images collection and search-images that contain results are Cache-Control: private, because their references are minted for each response: keep them in a per-user or per-session cache, never a shared one, and cache the records you resolve from them by stable id. The CDN serves image bytes with Cache-Control: max-age=7776000 (90 days); the image_url is stable, so let the browser or your image loader cache the bytes as long as it likes.
Images in Verse of the Day
Add language_ranges[] to any Verse of the Day request and the response carries an image_id for the day, whenever the language it resolves to has art for that day. Without the parameter, image_id is always null; day and passage_id are unchanged.
Endpoint URL
Code
Query Parameters
| Parameter | Required | Description |
|---|---|---|
day (path) | Yes, on the single-day route | Day of the year, 1–366. January 1 is day 1. A day outside that range returns 404. |
language_ranges[] | No | Ordered list of language ranges for the art. Omit it and image_id is null. The verse text itself is language-independent; you still pick a Bible when you fetch the passage. |
Example Request
Code
Example Response
Code
The full-year route returns the same shape inside a data array, one item per day.
Rendering it
Two calls get you from "what day is it" to pixels on screen:
Code
When image_id is null (the language your ranges resolved to has no art for that day, none of your ranges names a language with verse art, or you left the parameter off), show the passage text and move on. The response is still 200.
Language fallback, spelled out
| You send | You get art in |
|---|---|
language_ranges[]=en | English |
language_ranges[]=es-MX | Mexican Spanish if it exists, otherwise Spanish |
language_ranges[]=fr&language_ranges[]=en | French on days with French art, image_id: null on the others. English is used only if French had no verse art at all. |
language_ranges[]=* | English |
language_ranges[]=xx-ZZ (nothing matches) | image_id: null, text still returned |
Images in unified search
GET /v1/search-unified now returns a third result kind, images, next to verses and topics. Nothing about the existing request changes, including the required language_ranges[] (see Language selection for how search matches it); if you already call this endpoint, your responses simply grow an images array. This is the right call for a general search box that shows a strip of verse art under the verse results. It is unpaginated; when you need every matching image, use search-images.
Example Request
Code
Want only images from this endpoint? Narrow it with fields[]:
Code
Example Response
Code
Each image result is just id (a stable id) and passage_id. Resolve the id through GET /v1/images/{image_id} when the user taps it, or up front for the first few you plan to show. For everything else about this endpoint (bible_id access rules, user_intent, query metadata), see the Search APIs guide.
GET /v1/search-images
Search for verse art by query and page through the full result set. Like verse search, the bible_id determines the language searched, so there is no language parameter.
Endpoint URL
Code
Query Parameters
| Parameter | Required | Description |
|---|---|---|
query | Yes | The search query string (1–100 characters). |
bible_id | Yes | A Bible version your app can access (e.g. 3034). Determines the language searched. An inaccessible id returns 404 Bible version {id} not found; see Resource Not Found. |
page_size | No | Number of images to return, 1–99. Defaults to 25. |
page_token | No | The next_page_token from a previous response. |
Example Request
Code
Example Response
Code
Fetching the Next Page
When next_page_token is non-null, send it back as page_token with the same query, bible_id, and page_size. A token is bound to the search that produced it; reusing it with different parameters returns 400 Invalid page_token.
Code
Response Fields
| Field | Description |
|---|---|
images | Array of image results, each with id (a per-response reference) and passage_id. |
next_page_token | Token for the next page, or null when there are no more results. |
When nothing matches, this endpoint returns 200 OK with an empty images array and next_page_token: null, not a 204.
GET /v1/images
List verse art directly, without going through search. Filter by passage, by language, or both, and page through the results. Every item is the full Image record, so you can render it without a second call; resolve an item through GET /v1/images/{image_id} only when you need its stable id to store or deduplicate. This is the endpoint for "show me all the art for John 3:16" or "give me a gallery of Spanish verse images." Filtering works at the verse level: a single verse or a verse range. Whole-chapter filtering isn't supported yet.
Endpoint URL
Code
Query Parameters
| Parameter | Required | Description |
|---|---|---|
passage_id | No | Return images illustrating this passage: a single verse (JHN.3.16) or a verse range (1CO.13.4-7). An image matches if it illustrates any verse in the range. Chapter-level identifiers such as JHN.3 are not supported yet; pass a verse range that covers the chapter instead. A string that isn't a passage reference returns 400 Invalid passage_id. |
language_ranges[] | No | Filter by the art's language, tried in order. Resolution stops at the first tag the image library recognizes, even if it has no art for your filter, and a regional tag such as es-MX doesn't fall back to es; see Language selection. * returns every language. An unrecognized range format returns 400. Omit to return every language. |
page_size | No | Number of images to return, 1–99. Defaults to 25. |
page_token | No | The next_page_token from a previous response. |
Combining passage_id and language_ranges[] narrows to images matching both.
Example Request
Code
Example Response
Code
Response Fields
| Field | Description |
|---|---|
data | Array of full Image records. Each item's id is a per-response reference; resolve it through GET /v1/images/{image_id} if you need the stable id. |
total_size | Total matches across all pages. Only populated when you pass passage_id. A language-only or unfiltered browse returns null here because the catalog is large and an exact count isn't available; keep paging until next_page_token is null. |
next_page_token | Token for the next page, or null when there are no more results. |
A filter that matches nothing returns 200 OK with an empty data array (and total_size: 0 when passage_id was given), not a 204 or a 404.
Browsing a language
Leave off passage_id to walk a language's whole catalog. Expect total_size: null and a lot of pages:
Code
Page tokens are bound to the filter that produced them. You can change page_size between pages, but changing passage_id or language_ranges[] with an old token returns 400 Invalid page_token.
GET /v1/images/{image_id}
Resolve an id into the full Image record: a stable image_id from Verse of the Day or unified search, or a reference from the /v1/images collection or search-images. The record's id is always the image's stable id, even when you passed a reference.
Endpoint URL
Code
Path Parameters
| Parameter | Required | Description |
|---|---|---|
image_id | Yes | The opaque id exactly as the API returned it. |
Example Request
Code
Example Response
Code
Then load the bytes at the size you need, using image_url exactly as returned plus &size=:
Code
Setting width and height from the record (or a CSS aspect-ratio: 0.562) keeps the page from jumping while the image loads.
An id that doesn't resolve returns 404 Image not found, whether it was mistyped, forged, or belongs to an image that no longer exists.
Putting it together
A daily-verse card, start to finish:
- Call
GET /v1/verse-of-the-days/{day}?language_ranges[]={user language}&language_ranges[]=en. - Fetch the verse text for
passage_idfrom the passages API with the user's Bible. - If
image_idis non-null, callGET /v1/images/{image_id}and loadimage_urlwith&size=cardadded behind the text, withattributionin the corner. If it'snull, render the text on your own background, or repeat step 1 with onlylanguage_ranges[]=enif you'd rather show English art. - Cache each response for as long as its
Cache-Controlallows: a day for Verse of the Day and the Image record, and possibly less for passage text from a licensed Bible.
A "verse art" tab in your search results:
- Call
GET /v1/search-unified?...as you already do and render theimagesarray asthumb-size tiles, resolving each id as it scrolls into view. - When the user wants more, switch to
GET /v1/search-imageswith the samequeryandbible_idand page withnext_page_token. - On tap, show the
cardorfullrendition and offer thepassage_idas a link into the reader.
Optional: Bible App branding overlay
We publish the Bible App mark as a lower-third branding overlay for verse art. If you'd like your images to carry it, it's hosted on the CDN:
https://cdn.youversion.com/branding/bible-app-overlay-white.png
It's a 660 × 156 PNG on a transparent background: the Bible App icon beside a white "Bible App" wordmark, so it's meant to sit on top of the art rather than on a light page.
The overlay is entirely optional. YouVersion doesn't require it, and it doesn't replace attribution, which you still show wherever the image appears.
If you use it, these suggestions give the cleanest result:
- Layer it over the art at render time. Use a positioned element in HTML and CSS, an overlay view in a native layout, or a second draw call when you composite onto a canvas to export a share image. Both the art and the mark are served with
Access-Control-Allow-Origin: *, so load them withcrossOrigin = "anonymous"and the canvas stays exportable. - Keep it in the lower third, clear of the verse text on the art, with some padding from the edge.
- Size it relative to the image, not in fixed pixels, so it scales with
thumb,card, andfull. A width of roughly a third of the image is a good starting point. The file is 660 pixels wide, so it stays sharpest when it's drawn at or below that many device pixels; on a 2× screen, that's about 330 CSS pixels. - Keep its proportions. Scale it evenly rather than cropping or stretching it.
- Mind the contrast. The wordmark is white. Where the bottom of an image is light, put a subtle dark gradient behind the mark, or leave the mark off that image.
- Skip it where it can't be read, such as
thumb-size tiles in a dense grid.
On a web page, that can be as little as an absolutely positioned image inside a wrapper, with an optional gradient behind it:
Code
The mark is decorative, so give it an empty alt and keep the passage in the main image's alt text. It's the same small file for every image, and the CDN serves it with a one-day Cache-Control lifetime, so your browser or image loader will reuse one copy across every image it decorates.
Errors
The Images endpoints use the standard Platform error responses:
| Status | When | Examples |
|---|---|---|
400 Bad Request | A parameter is syntactically valid but rejected. | passage_id that isn't a passage reference; malformed language_ranges[] value; a language_ranges[] on search-unified that matches no language search supports; page_token reused with different parameters or corrupted. |
401 Unauthorized | Missing or invalid App Key, or a key not enrolled in the beta channel. | No X-YVP-App-Key header; oauth.v2.InvalidApiKeyForGivenResource on /v1-beta for a non-enrolled key. See Authentication. |
404 Not Found | The resource isn't available to your app. | image_id that doesn't resolve (Image not found); a Verse of the Day day outside 1–366 (Not Found); on search-images / search-unified, a bible_id your app can't access (Bible version {id} not found). |
422 Unprocessable Content | A required parameter is missing or out of range. | Missing query or bible_id on search-images; missing language_ranges[] on search-unified; page_size outside 1–99. |
429 Too Many Requests | Rate limit exceeded. | Check Retry-After before retrying. |
502 / 503 | The upstream image or search service is unavailable. | Retry with backoff; Verse of the Day returns 503 Verse images are temporarily unavailable when only the art lookup fails. |
The CDN returns a plain-text 400 for an invalid or duplicated size parameter; see Picking a size.
For response body shapes, see the error codes reference.
Need Help?
If you need help integrating verse images or have questions about the documentation, please reach out to our support team.