From 50 Minutes to One Command. 220 Times.
I built a pipeline that translates, assembles, and publishes Bible reading plans on YouVersion. What used to take 50 minutes of manual work per plan now takes about a minute of review and one click to publish. 220 plans shipped, an undocumented API reverse-engineered, an entire publishing-to-acquisition loop automated.
Context. HelloBible's global expansion had one bottleneck: 110 French reading plans that had to ship in every new language before acquisition could start on YouVersion. At 40-50 minutes per plan manually, that was months of blocked growth.
Result. 220 plans live on YouVersion (110 English + 110 Dutch). 110 French source plans audited and cleaned via the same pipeline. Cost: ~40€/month in DeepL Pro API. My time per new plan: about a minute of review and publish after the run. The pipeline scales to any language by swapping a Bible version ID.
YouVersion has over 600 million installs. For HelloBible, every published reading plan ends with a bit.ly link routing readers back to the app - it’s one of the most direct acquisition channels available to a Bible app. The problem: all 110 plans were in French only. No English, no Dutch - the channel existed but couldn’t be used at scale.
I built the pipeline that unblocked it: translation, Bible verse matching per locale, image generation, and publication to YouVersion in one command.
Key results:
- 220 plans live on YouVersion (110 English + 110 Dutch)
- ~180 hours of manual work eliminated
- ~40€/month in DeepL Pro API - everything else free
- New language: a few days of configuration, no structural changes
- Every plan routes readers back to HelloBible via bit.ly
The friction I found
Each reading plan is 3-7 days of devotional content, cited Bible verses in context, two branded images per plan. Publishing one plan in a new language isn’t just running text through a translator. Each language requires:
- Translating the devotional text
- Finding the official Bible version for every cited verse (NIV for English, HSV for Dutch - you can’t translate the Bible itself, only the surrounding content)
- Rebuilding the plan structure in the YouVersion Partner Portal
- Sourcing and resizing two images per plan
- Validating everything visually before publishing
Time per plan, end to end: 40 to 50 minutes. Across 110 plans × 2 languages, that’s roughly 180 hours of manual work. And that’s just to start - the whole point is global expansion across dozens more languages.
There was one more friction nobody told me about until I started building: the YouVersion Partner Portal API is undocumented. No public reference, no SDK, no examples. I had to reverse-engineer the endpoints, payload shapes, and validation rules by intercepting network requests from the web UI and watching what worked. That alone took hours of focused work before any “real” code could be written.
From POC to production stack
My first step was a POC on N8N - a quick way to validate the logic before committing to a full build.
After that POC, I chose to move the entire pipeline to a standalone Node.js script driven by direct API calls. Three reasons drove that decision: eliminating the dependency on third-party infrastructure, running the full pipeline locally without external orchestration overhead, and having granular control over raw data processing that a visual workflow tool couldn’t give me.
How the pipeline replicates
- New language: update translation settings and Bible version mappings (NIV for English, HSV for Dutch), run the pipeline. A few days of configuration - the architecture doesn’t change per language.
- Audit and clean source plans: I ran the 110 French originals through the same tooling to enforce consistency (Bible verses italicised, founder name bolded, all images regenerated to a new visual standard).
- New partner: the same pipeline pattern could work for any creator publishing to YouVersion. Estimated port: about a week of adaptation, not yet tested.
The technical architecture, if you want it
Stack
- Node.js ES modules, dotenv config
- DeepL Pro for the translation itself (not Claude - more on this below)
- Bible.com webproxy API for fetching official Bible verses by USFM reference
- YouVersion Partner Portal API (
planportal.youversionapi.com/4.0) for creating and updating plans - Canvas + Cairo with embedded Playfair Display TTF for image generation
- Pexels API for sourcing photos by theme
- MD5 caches for DeepL translations, Bible verses, and Pexels photos (cohérence between EN and NL versions of the same source plan)
- JSON files for progress tracking and idempotence
Why DeepL and not Claude for the translation
DeepL leads on European language quality, supports HTML mode natively (tag_handling: 'html' with ignore_tags: ['x'] to protect verse placeholders), and is significantly cheaper at scale than running translations through an LLM. Quality is also more consistent for this kind of long-form devotional content.
Claude is my coding assistant for the project, not a runtime component of the pipeline.
The non-trivial part: not translating the Bible
You can’t translate Bible verses. Each language has officially approved versions (NIV for English, HSV for Dutch since NBV21 was pulled in 2021). Translating them via LLM or DeepL would be theologically and legally wrong.
So the pipeline:
- Extracts Bible verses from the source HTML
- Replaces each one with a placeholder (
VPHOLDER0,VPHOLDER1…) - Sends the HTML with placeholders through DeepL
- Fetches the official target-language version of each verse from Bible.com using its USFM reference
- Inserts the official verses back into the translated HTML
- Re-syncs the references panel that YouVersion shows separately from the plan body
This step is what makes the pipeline non-trivial. Anyone can call DeepL. The interesting work is keeping the sacred text untouched while everything around it translates.
The reverse-engineered API
YouVersion’s Partner Portal has no public API documentation. To build the pipeline I had to:
- Inspect network requests in the web UI to map every endpoint
- Trial-and-error on payload shapes until validations stopped failing
- Identify hidden requirements (e.g. references panel must be synced separately from HTML body)
- Find the right status flow (draft → approved → published)
- Discover undocumented constants (Polly voice IDs, Bible version IDs, org IDs) by reading actual data on the platform
This is a few hours of work that doesn’t show up in any line count - but without it, nothing else ships.
Domain knowledge encoded
- USFM mapping for ~50 Bible books across French, Dutch, English (
Genèse → GEN,Mattheüs → MAT, etc.) - Bible version IDs (NIV=111, HSV=1990, BGT=3743)
- Polly voice IDs per language for audio narration
- YouVersion org ID (823 for Eric Célérier)
- 30 regex patterns to detect plan themes (lion, shepherd, water, peace, mountain…) for matching Pexels image queries
- Brand-specific patterns: stripping promotional P.S. blocks, preserving brand hashtags untranslated, gras “Eric Célérier” handling
Flow end-to-end per plan
- Load source plan (French) from local JSON
- Translate title, description, keywords via DeepL in parallel
- POST createPlan to YouVersion → get plan ID back
- PATCH plan metadata (CTA link, Polly voice ID, Bible version ID)
- For each day:
- Concatenate multiple content blocks (one of the bugs I had to fix)
- Clean HTML (strip P.S., Conseil, stars)
- Protect verses with placeholders
- Translate via DeepL
- Fetch and restore official verses
- Patch the day into YouVersion
- Generate 1440×810 hero image and 320×320 thumbnail via Canvas + Pexels (cached per source plan ID for EN/NL consistency)
- Upload images, set plan images
- Save progress to
data/progress.json - Re-publish via separate
republish-plans.jsscript
What broke
Every interesting pipeline has a list of bugs that nearly killed it. The honest ones:
- DeepL Free quota exhausted silently because an auto-test was running on every script import - draining quota without me knowing. Caught it when plans started coming out empty.
- French verses skipping translation in 10 plans because my
isFrench()detector missed phrases without accents. - Multi-block days collapsing to the first block - my
.find()only captured the first text block, so plans with multiple text blocks per day showed only the title. Affected 4 plans. Had to write a separate fix script. - Long Dutch titles overflowing 320×320 thumbnails - words like “Ondoorgrondelijke” (19 characters) needed 3 levels of adaptive font sizing.
- DeepL “Quota exceeded” with quota actually remaining - turns out you have to set the spending cap explicitly in DeepL Pro settings. Nowhere obvious in the docs.
Where this lives now
Pipeline operational. 220 translated plans live, plus the 110 French source plans audited and cleaned. Code on GitHub. Each bug from the section above became a guard in the pipeline, so new plans now run end-to-end with the known failure modes guarded. Maintenance is minimal - mostly running the pipeline for new plans and new languages as they come in.
Adding a new language requires updating translation settings and Bible version mappings. No structural changes to the pipeline.
Want to talk about something like this?
Email me, send a LinkedIn message, or download the CV. Conversations are what this site is built for.