PDFtoMD
Behind the BuildClaude CodeNext.jsStripe

How We Built a PDF-to-Markdown SaaS in 48 Hours with Claude Code

A full behind-the-scenes look at building PDFtoMD: the stack (Next.js, Supabase, Stripe), the architecture decisions, the mistakes, and what we learned shipping an AI-powered SaaS solo.

10 min readBy Rafael Abellan

Most behind-the-build posts get written months after launch, once the rough edges have been sanded down and the story has been tidied into something that sounds inevitable. This one is different. I am writing it while PDFtoMD is still young, while I still remember the exact moments where things broke, and while the lessons are still a little embarrassing.

PDFtoMD started as a weekend project. The core product, the part that takes a PDF and hands you back clean Markdown, was built and deployed in roughly 48 hours. The stack was Next.js, Supabase, Stripe, and the Claude API. The pair programmer for the whole thing was Claude Code. Here is how it came together, what I got wrong, and what I would tell anyone trying to ship a small SaaS on their own.

Why I Built It in the First Place

I kept hitting the same wall. I would drop a PDF into an AI chat, ask a question about it, and watch a big chunk of my context budget vanish into layout noise: font tables, positioning data, and page furniture that had nothing to do with the actual words on the page. If you have ever wondered why a 50-page document drains your tokens so fast, I wrote about the math behind that in the Claude AI guide.

The fix was always the same. Convert the PDF to Markdown first, then paste the Markdown. Clean text, a fraction of the tokens, noticeably better answers. I was doing this by hand often enough that it stopped feeling like a workaround and started feeling like a product. So I gave myself a weekend to find out whether anyone else would want it.

The 48-Hour Box

The time limit was not a stunt. It was a forcing function. When you only have a weekend, you cannot afford to argue with yourself about the perfect database, the perfect framework, or the perfect folder structure. You pick boring tools you already trust, you cut scope ruthlessly, and you ship.

Here is what made it into the first version:

  • Upload a PDF and get clean Markdown back.
  • A free tier so people can try it without a credit card.
  • Accounts, so usage could be metered.
  • A paid plan, so the thing could actually make money.

And here is what I deliberately left out: batch processing, an API, team accounts, folders, history, OCR for scanned documents, and roughly thirty other ideas that felt essential and were not. Every one of those could come later, once I knew people wanted the basic thing. Cutting scope is uncomfortable. It is also the only reason the weekend worked.

The Stack, and Why Each Piece

None of these choices are clever. That is the point. A weekend build is not the place to learn a new framework.

  • Next.js on Vercel: One framework for the frontend, the API routes, and the marketing pages. Push to Git, and Vercel deploys it. No servers to manage.
  • Supabase: Postgres, authentication, and row-level security in one box. I did not want to hand-roll auth on a Saturday.
  • Stripe: Checkout and subscriptions. It is the default for a reason, and the webhook model maps cleanly onto a metered SaaS.
  • Resend: Transactional email for the account flows. Simple to wire up, and it stayed out of my way.
  • The Claude API: The actual conversion engine. Instead of fighting with brittle PDF parsing rules, I let a capable model read the document and return well-structured Markdown.

Every piece here is boring on purpose. The novelty in the product is the conversion quality, not the infrastructure. Spending my limited hours on plumbing would have been spending them in the wrong place.

Claude Code as the Pair

When the title says we, the we is me and Claude Code. I was the only human on this project, but I was not working alone. The loop looked like this: I described what I wanted in plain language, Claude Code scaffolded it, I read the result carefully, and I corrected the parts that were wrong or that I simply did not like. Then we did it again.

I want to be honest about what that is and is not. It is not magic, and it does not replace understanding your own codebase. When something broke, I still had to know why. Claude Code did not save me from owning the architecture or the decisions. What it did save me from was the slow, mechanical part: wiring up a Stripe webhook handler for the fourth time, remembering the exact shape of a Supabase policy, or scaffolding a settings page from scratch. It compressed the boring 80 percent so I could spend my attention on the 20 percent that actually mattered.

The mental shift is that you stop writing every line and start reviewing every line. That is a different skill, and it is the one worth getting good at.

PDFtoMD is the tool I built over that weekend. Convert your first PDF to clean Markdown for free.

How the Conversion Actually Works

The flow is deliberately simple. A user uploads a PDF. The text is extracted and handed to Claude with a prompt that asks for faithful, well-structured Markdown: real heading hierarchy, preserved lists and tables, and none of the layout junk. The Markdown comes back, the user copies it or downloads the file, and that is the whole product.

The interesting engineering is not the parsing, it is the metering. The free tier gives you three conversions a month, which means the app has to count usage reliably and reset it on a schedule. That counter lives in Supabase, and the paid plan flips a flag through a Stripe webhook. Most of the real bugs in the early days were not in the conversion at all. They were in the accounting around it. If you are building anything similar, that clean Markdown is also exactly what you want feeding a RAG pipeline later, which is a big part of why people use it.

The Mistakes (the part you actually came for)

The build took a weekend. The mistakes took considerably longer to find. These are the real ones, not the tidy ones.

Mistake 1: Supabase quietly paused itself

One morning the login page just returned a failed to fetch error. No deploy had gone out, no code had changed, nothing in my logs explained it. The cause turned out to be a feature, not a bug: on the free Supabase plan, a project pauses itself after about a week of inactivity in the dashboard. The fix was a single click to resume, plus a recurring reminder to log in once a week so it does not happen again. The lesson is that the free tier of any managed service comes with rules, and those rules will surprise you at the worst possible time if you have not read them.

Mistake 2: Google saw four different homepages

When I finally looked at Search Console, my homepage was indexed as four separate URLs: http and https, with and without the www prefix. Each variant split the ranking signals that should have been pooling into one address. To Google it looked like four weak pages instead of one strong one. The fix was setting an explicit canonical URL on every page and adding a permanent redirect so every variant collapses into a single address. If you are not deliberate about canonical URLs, the search engine will guess, and it tends to guess in a way that dilutes you.

Mistake 3: I nearly trusted the wrong Stripe mode

Stripe has a test mode and a live mode, and they look almost identical. For a while I was not fully certain which one production was actually using, which is a deeply uncomfortable thing to be unsure about when real money is involved. The resolution was boring but necessary: confirm it directly, with a test card that should be rejected in live mode getting rejected, before trusting that a single dollar could be charged. When payments are on the line, verify the obvious thing instead of assuming it.

Mistake 4: one line of code froze every deploy for days

This is the most recent one, and the most instructive. I published a new blog article, pushed it, and moved on. What I did not notice was that the article contained a small type error in a code sample. That single error failed the build, and because the build failed, the deploy never completed. Production simply froze on the previous version. For several days, everything I pushed looked successful from my side and changed nothing on the live site. The article I thought I had published was returning a not-found error, and I had no idea.

I only caught it because someone looked at the live blog and noticed two articles stuck as coming soon when there should have been one. The fix itself took minutes. The damage was the silence: a broken deploy that announced itself to no one. The lesson burned itself in. Shipping is not pushing to Git. Shipping is confirming the change is actually live, and having something that shouts when it is not.

What Shipping Solo Actually Taught Me

A few things became obvious only after launch, the kind of things you cannot learn from a tutorial.

The first version is fast. Done is slow. Getting to a working product in 48 hours felt great, and it was genuinely the easy part. The long tail of edge cases, billing states, SEO, and the boring reliability work is where most of the real time goes. Anyone who tells you the build is the hard part has probably not shipped much.

Distribution is the actual job. A converter that works perfectly and that nobody can find is a hobby, not a business. The weekend bought me a product. Everything since then, including the article you are reading right now, is the slower work of helping people discover it. I underestimated that gap badly, and I suspect most technical founders do.

Small apps still need observability. I assumed a project this size did not need monitoring. The frozen deploy proved me wrong. You do not need a giant dashboard, but you do need a way to be told when the thing you believe is live is not.

The Tools Are Not the Story

It would be easy to read this as a post about Claude Code, or about a particular stack. It is not. The tools made the weekend possible, and I would absolutely build this way again, but the tools were never the point. The point was picking a problem I personally felt, boxing the build so tightly that I had no room to overthink it, and then accepting that launch is the beginning of the work rather than the end of it.

If you have an itch like that, a small thing you keep doing by hand, you can probably build the first version of it faster than you think. The weekend is the fun part. Just go in knowing that the weekend is also the smallest part of the story.

Try the Thing I Built

PDFtoMD is live, it is free to start, and it is the direct result of that weekend. If you work with PDFs and feed them to AI tools, note-taking apps, or documentation systems, you can see the full list of use cases here, or just upload a document and watch it come back as clean Markdown.

Rafael Abellan

About the author

Rafael Abellan

Founder, PDFtoMD

Rafael Abellan is the founder of Agência Triva and ships independent side projects in parallel. PDFtoMD came out of a personal frustration: he kept burning through Claude AI's token limit by uploading long PDFs, then losing hours waiting for the cap to reset. He built the tool to fix his own workflow, and now uses it every day.

Ready to convert your PDFs to Markdown?

Free account · 3 conversions/month · No credit card required