Replacing Upwork with LLM Automation
Every year, I outsource a 2-3 days task to Upwork. It’s not complicated, but it’s tedious work. And every year I have to adapt the job posting to describe the job in detail. It costs around $40.
Yesterday I used Claude Code to automate the task. It took me around 50 minutes, the cost a little over $5. Running the script to perform the task costs zero. The script is written so that I can run it every time every year with no modification.
Makes me wonder if I’m going to use Upwork ever again for this kind of work.
Read along for the full story.
Since 2022, my wife has been publishing a daily journal titled Living with Purpose. It’s structured around reflecting about your life, clarifying your values and goals, and bringing it down to your day to day.
The journal is published as a PDF. Each day of the year has its own page in the booklet. There is a page with the whole year’s calendar, where each day is hyperlinked to the corresponding page. Also, most of the pages contain a link to return to the table of contents, and another link to go to the notes section of the booklet.
Each year around October, after finishing next year’s edition using Canva, she downloads the booklet as a PDF. Then comes the tedious part: creating the 400+ internal links between the different parts of the document.

What we usually do a freelancer through Upwork to do the job. Writing the job description for the job posting is tedious, and it takes around 30-40 min. From previous experience, few freelances get the gist of the work the first time. So I need to specify precisely which text in each page links to where. Also, I attach screenshots so everything is clear and there is no doubt about the job to be done.
When the freelancer finishes, we check if the links has been created properly, which also takes time.
You can guess where this story is going, right?
This year, I decided to try Claude Code to insert the links. Claude Code is an AI coding assistant that runs in your terminal, writing and executing code based on natural language instructions. While Claude cannot manipulate the PDF file directly—it weights more than 300MB—it can write code to do the manipulation.
There are three different kind of links in the Journal:
- Many of the pages have a rectangle with the text “NOTES” on the lower right corner, and another with the text “HOME” on the upper right. These rectangles need to link to specific pages in the PDF. Around 400 links.
- There is a table of contents with around 20 entries. Each entry needs to link to the appropriate page. I could have done these by hand if needed.
- Then, there is the annual calendar. Each day of each month needs to link to the the page for that day. 377 links.
I was sure the LLM would get them right the first two group of links. I was not so sure about the calendar links, however. But I was willing to try.
I started with a simple prompt:
The folder contains a PDF file named @"2026 Daily Journal.pdf". Many of the pages contain a vertical rectangle in the lower right corner with the text "NOTES". I need all those rectangles with the text NOTES to link internally to page 429 of the PDF.
Write a Python script to do that. Use `uv` for package management.
Add the following functionality: there are "HOME" rectangles in the upper right corner of many pages. Add an internal link from all "HOME" rectangles to page 5.
I wanted it written in Python because that is what I use most, but I could have omitted that line and let the LLM decide which programming language to use.
It worked perfectly, the first time.
Next, the table of contents:
Now add the following functionallity: On page 5 there are several rectangles with text. This page serves as a table of contents to the rest of the document. Add the links for the following rectangles. Example: "Cover Page": page 1 is the rectangle with the text "Cover Page" and it should link to page 1.
* "Cover Page": page 1
* "How to start?": page 6
(and so on for 25 more pages/links)
Again, it worked the first time.
Then came the tricky part: the yearly calendar.

I wrote the following prompt:
For each month's label on page 7, add a link to the corresponding month's cover page. The link should be in the form of an invisible rectangle around the month's label, so that it works well if using a finger on a tablet.
For each day of each month on page 7, add a link to the corresponding page for that month's day. You can calculate where that page is by offsetting from the month's cover page. For example, the page for January 1st is 52. Make sure that the links for each day on page 7 is a rectangle so it's easy to "click" on it with the finger or a stylus. Also, make sure that the links don't overlap.
This time the script correctly created the links for most of the days. I had to test each link by hand. (The same I had to do with the Upwork job, by the way.)
I made a list of the missing of incorrect and missing links, so that Claude Code refined the script.
Correct these errors:
January: All links are correct.
February:
* February 1st is not linked.
March:
* March 1st links incorrectly to the February 1st page.
* March 8, 15, and 22 don't link to their respective pages.
* March 29 links to the March cover page instead of to the March 9 day page.
April:
* April 5, 12, 19, and 26 (the first column of April's calendar) don't link to their respective pages.
May:
* May 3, 10, 17, 24 and 31 (the first column of May's calendar) don't link to their respective pages.
June:
* June 7, 14, and 21 don't link to their respective pages. June 28 links to May 28 instead of the June 28 day page.
July:
* July 5, 12, 19, and 26 don't link to their respective pages.
August:
* August 2, 9, 16, 23, 30 don't link to their respective pages.
September: All links are correct.
October: All links are correct.
November: All links are correct.
December: All links are correct.
Also, the link areas appear to be of different sizes. Sometimes when I hover over the calendar day's number on page 7 the cursor transforms to a hand correctly, other times I need to click exactly on the number and the cursor doesn't change from arrow to hand.
This time, Claude Code wrote an auxiliary script to test that the links were correctly generated. It corrected most of the errors. Again, I made a list of the few errors remaining and feed them to Claude Code. This time, the script got all the links correctly inserted.
It took me around 40-50 minutes total of tinkering with Claude Code to insert the links. The total cost was $5.03.

If I had used Upwork, I would have spent at least 30 minutes writing the job description for the task. I would have had to select a freelancer from the ones applying for the job, besides suffering Upwork avalanche of mails. The job itself would have taken at least a couple of days, plus corrections. (In three years, no freelancer has got the job done well the first time). Last year, we paid $40 for the job.
More important, now we can use the same script to insert the links for 2027’s journal. It will cost $0 and take only a couple of minutes to run.
If you’re curious, I’ve uploaded the code and a prompt synthesized by Claude to this repository.