The $1 Tech Stack: How I Connected Claude and DeepSeek APIs for Massive Data Extraction (Without Writing Code)

AI Export Lab · July 2026

Before this project, I had never used an API. I had never written Python. I had never opened a terminal. I am a TCM practitioner building an auto parts export site after work. In two evenings, using Claude to write every line of code and DeepSeek's API to process 500 PDF pages, I turned a supplier catalog into 15,000+ CSV rows. The API bill was under 5 RMB — less than one US dollar. This is exactly how I set it up, what broke, and what I would skip if I had to do it again.


The First Thing That Went Wrong

Let me start with a failure because the tutorials never do.

I set up my DeepSeek API key. I asked Claude to write a Python script. I ran it. The terminal filled with a red error message I did not understand.

The problem: I had installed Python but missed the checkbox that says "Add Python to PATH." On Windows, this means you can type python in PowerShell and nothing happens. The fix is checking one box during installation. But no tutorial mentioned this, because people who write tutorials installed Python five years ago and forgot this step exists.

This is not the interesting kind of failure. It is the stupid kind. But it is the kind that stops non-programmers cold. I nearly quit on day one because of a checkbox.

If you are reading this and have never installed Python: go to python.org, download the installer, and check the box labeled "Add Python to PATH." Everything else in this article assumes you did that. If you skip it, nothing works and the error messages will not tell you why.

OK. Now the part that actually worked.


The Actual Setup (15 Minutes, $6 Loaded, $1 Spent)

I used two AI APIs: DeepSeek for the bulk vision processing, Claude for writing the script and handling edge-case pages. Here is how to get both:

DeepSeek API key:

  1. Go to platform.deepseek.com, create an account.
  2. Click "API Keys" in the sidebar → "Create new key." Copy it. Paste it into a plain text file named api_key.txt.
  3. Top up 10 RMB (about $1.40). This turned out to be more than enough for 500 pages.

Claude API key (optional):

  1. Go to console.anthropic.com, create an account.
  2. "API Keys" → "Create Key." Save it.
  3. Load $5. I only used Claude for the hardest 10% of pages — the ones where DeepSeek's output wasn't clean enough.

That is the entire setup. No server. No cloud configuration. No command-line wizardry. Two websites, two passwords, $6 loaded, about $1 actually spent on processing. DeepSeek's vision pricing is absurdly cheap — I wasn't careful about token optimization and the bill still barely moved.


How Claude Wrote the Script (And What Broke)

I wrote zero lines of Python. I described the task in plain English. Here is the prompt that produced the first working version:

I have a folder of PNG images. Each image is one page from an auto parts PDF catalog. The pages have a consistent layout: 3 columns, about 8 products per page. Each product has a part number in the upper-left corner, a short description, dimensions, and vehicle fitment info.

Write me a Python script that:
1. Reads every PNG in a folder named "catalog_pages"
2. Sends each image to the DeepSeek API (vision model)
3. Uses this system prompt: "Return ONLY valid JSON. Each product should have fields: part_number, description, dimensions, fitment. If unreadable, use null. Do not guess."
4. Saves everything to products.csv

I am on Windows. Python is installed. My DeepSeek API key is in api_key.txt in the same folder.

Claude produced about 60 lines of Python. I did not understand most of it. I ran it anyway.

It crashed. Five times.

Crash #1: JSON decode error. DeepSeek returned something like "Sure! Here is the extracted data:" followed by the JSON. My script tried to parse the whole thing as JSON and died. Claude's fix: strip everything before the first curly brace. One-line change. Took 30 seconds.

Crash #2: Markdown code fences. Some pages came back with the JSON wrapped in ```json ... ```. The parser choked on the backticks. Fix: add a cleanup step that removes code fences before parsing.

Crash #3: Null fields causing CSV writer to fail. When a field was genuinely unreadable and the model returned null, the CSV writer threw a type error instead of writing "null" as a string. Fix: convert null to empty string before writing.

Crash #4: API rate limiting. I had no delay between requests. Around page 40, DeepSeek started returning HTTP 429 errors. Fix: add time.sleep(0.5) between pages. Processing time went from 8 minutes to 15 minutes. Worth it.

Crash #5: A page with zero products. One page in the PDF was a section divider — no products, just a title. The model returned an empty array, which was correct, but the CSV writer expected at least one row and produced a malformed file. Fix: skip writing CSV rows when the products array is empty.

Each fix was one follow-up message to Claude. The whole debugging loop took 30 minutes. By the end, the script processed 500 pages without human intervention — and when it did hit a parsing error, it saved the raw response to a file for manual review instead of crashing.

The key insight: I did not debug. Claude debugged. I described what I saw on the screen in plain English, and Claude rewrote the code. That loop — see red text, describe it, get a fix — is the actual workflow. Not "AI writes perfect code first try."


The Working Script

Here is the final version, with all five crash fixes baked in. Copy it. Break it. Fix it the same way I did:

import os
import json
import base64
import requests
import csv
import time

with open("api_key.txt", "r") as f:
    api_key = f.read().strip()

url = "https://api.deepseek.com/v1/chat/completions"
headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

system_prompt = """You are extracting product data from an auto parts catalog page.
Return ONLY valid JSON. No markdown. No explanation.
Format: {"products": [{"part_number": "...", "description": "...", "dimensions": "...", "fitment": "..."}]}
If a field is unreadable, use null. Do not guess."""

all_products = []
image_folder = "catalog_pages"

for filename in sorted(os.listdir(image_folder)):
    if not filename.endswith(".png"):
        continue

    with open(os.path.join(image_folder, filename), "rb") as f:
        image_data = base64.b64encode(f.read()).decode("utf-8")

    payload = {
        "model": "deepseek-chat",
        "messages": [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": [
                {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_data}"}},
                {"type": "text", "text": "Extract all products from this catalog page as JSON."}
            ]}
        ],
        "temperature": 0.1
    }

    response = requests.post(url, headers=headers, json=payload)
    data = response.json()
    content = data["choices"][0]["message"]["content"]

    # Fix #1 and #2: strip conversational wrappers and code fences
    content = content.strip()
    if "{" in content:
        content = content[content.index("{"):]
    if "}" in content:
        content = content[:content.rindex("}") + 1]
    content = content.replace("```json", "").replace("```", "").strip()

    try:
        result = json.loads(content)
        products = result.get("products", [])
        if products:  # Fix #5: skip empty pages
            all_products.extend(products)
            print(f"Page {filename}: {len(products)} products")
        else:
            print(f"Page {filename}: no products found (section divider?)")
    except json.JSONDecodeError:
        print(f"Page {filename}: JSON parse failed — saved for manual review")
        with open(f"failed_{filename}.txt", "w", encoding="utf-8") as f:
            f.write(content)

    time.sleep(0.5)  # Fix #4: don't get rate-limited

# Fix #3: null → empty string for CSV compatibility
with open("products.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["part_number", "description", "dimensions", "fitment"])
    writer.writeheader()
    for product in all_products:
        writer.writerow({k: (v if v is not None else "") for k, v in product.items()})

print(f"\nDone. {len(all_products)} products saved to products.csv")

This is not elegant. It is not production code. The error handling is crude. But it processed 500 pages into 15,000+ CSV rows while I made tea. For a non-programmer working alone after hours, that is the only metric that mattered.


The Prompt Template I Arrived At (After 500 Pages)

After watching DeepSeek's output across hundreds of pages, I learned exactly what makes a prompt produce clean, parseable JSON vs. garbage that breaks your script. Here is the template:

You are extracting structured data from a [DOCUMENT TYPE].
Return ONLY valid JSON. No markdown. No explanation. No "here is the output."

Format: {"items": [{"field_1": "value", "field_2": "value"}]}

Rules:
- If a field is unreadable, use null. Do not guess.
- If a field does not exist on this page, use null. Do not hallucinate.
- Preserve exact codes and identifiers — dashes, slashes, and punctuation matter.
- Ignore page headers, footers, watermarks, and page numbers.
- If the page contains no valid items, return {"items": []}.

Each line of this template exists because a previous version of the prompt failed in a specific way. Here is what happened without each rule:

These rules look obvious in hindsight. They were not obvious when I started. Each one was added after I found a specific failure in my CSV and traced it back to the prompt.


What I Would Skip If I Did This Again

I wasted time on things that did not matter. Here is what I would not repeat:


Why Two Models Instead of One

I did not pick between Claude and DeepSeek. I used both for different jobs:

The principle is the same as hiring: you do not pay a senior engineer to do data entry. Use the cheap tool for volume, the smart tool for hard problems. This is not a technical decision. It is a cost decision — and it is the reason the whole project cost under a dollar instead of $20.

If I had run all 500 pages through Claude's API at standard pricing, the bill would have been somewhere around $15-25 depending on resolution. Still cheap compared to hiring a VA. But $1 sounds a lot better than $20 when you are a solo operator testing whether an idea even works.


Still Staring at a Supplier Catalog?

If you have a PDF that needs to become a database and do not know whether AI vision will work on your specific document layout, send me a 2-page sample through the Contact page. I will tell you honestly what accuracy to expect — and whether you are better off with this approach or just hiring someone to type it manually.

Most people pick wrong because they do not test first. Two pages is enough to know.


Related Articles

Back to AI Export Lab — every build note from this experiment.