ResumeJSON

Bulk resume parsing: how to run a CV backlog

Bulk resume parsing: how to run a backlog of CVs without blowing the bill

Bulk resume parsing is almost never a bulk endpoint. Every major parsing API in this category, ours included, takes one document per request, so "parse 40,000 CVs" is a loop you write, a concurrency number you pick, and a retry policy you own. Get those three right and a backlog of 40,000 finishes overnight for a predictable amount of money. Get them wrong and you pay twice for the same documents.

This article is for the developer who has an archive to ingest: a job board importing a candidate database, an applicant tracking system onboarding a customer, a matching prototype that needs 10,000 profiles before it can be evaluated. It covers what the vendors actually allow, what a failed call costs you, how to size the run before you start it, and where a per-parse API is cheaper than a credit plan for this specific shape of work.

We build ResumeJSON, one of the APIs in the comparison, so read this as written by an interested party. Every claim about somebody else's product below was read off that vendor's own pages in September 2026, and their pages are theirs to change.


The first thing to check: does the vendor let you go parallel?

This is the question that decides your wall-clock time, and it is answered in documentation rather than on the pricing page. It is worth reading before you write the loop, because the answer is sometimes no.

Textkernel's developer FAQ, as of September 2026, is explicit about it:

Routine parsing of batches of less than 100,000 documents MUST always be done serially (one-at-a-time).

Read that as a real constraint on a migration plan. At one document at a time, a run's duration is the number of documents multiplied by the per-document latency, with no lever to pull. If the per-document time is two seconds, 40,000 documents is about 22 hours of continuous serial calling. If it is ten seconds, the same backlog is five days.

The other half of that policy is what happens when you ignore it. Textkernel's platform documentation states that a request refused with HTTP 429 for exceeding the concurrency allowance in their acceptable use policy is still charged, and that resubmitting it is the caller's job. So a loop that opens twenty connections to go faster can spend real credits on refusals and finish later than the serial loop it replaced.

What a failed call costs

Most bulk runs are sized on the happy path, and the failures are what break the estimate. Three kinds of failure cost money, and they are worth separating before you start:

The practical rule: make your loop idempotent on your side. Keep a table with one row per source document, a status column, and the request id of the call that produced it. Re-running the job then skips what already succeeded, and a retry is something you decide rather than something a catch block does for you. Every parsing API in this category returns a request id you can store; ours puts it in the response body and in an X-Request-Id header.

Sizing the run before you start it

A bulk job has four numbers. Work them out on paper first, because three of them are cheap to change before the run and expensive to change during it.

NumberHow to get itWhy it bites
DocumentsCount the archive, not the rowsDuplicates and non-CV attachments are routinely 10% to 30% of an old archive
Per-document timeParse 50 real files from the archive and take the medianA vendor's advertised latency is measured on their own sample, and scanned files are slower than text ones
Concurrency allowedThe vendor's acceptable use policy, not the pricing pageExceeding it can be charged, and the limit is often 1
Price of one documentBase price plus every add-on you will actually switch onA credit is not a parse when normalization and geocoding are billed on top

The fourth one is where credit plans and per-parse plans diverge. On a credit platform, a parse is a base cost and then a stack of add-ons. Textkernel's published transaction-cost table, as of September 2026, prices a resume or job parse at 1 credit, then adds 0.2 credits for job title normalization, 0.1 for skills normalization, 1 for geocoding with default options (0.1 if you supply your own provider key), 0.05 for resume OCR, and 0.1 for its LLM parser. Switch on two normalizations and default geocoding and the same document is 2.3 credits rather than 1, so a 40,000-document run is 92,000 credits.

That arithmetic is not a criticism of credits. It is a reason to do it before the run rather than after the invoice.

What the bulk run costs on each shape of plan

Credit plan (Textkernel)Per-parse API (ResumeJSON)
Buying modelCredits purchased in advance, monthly plans from 500 to 25,000 credits, annual up to 100,000Pay per use, or a monthly quota with priced overage
Entry priceProfessional, from $99 a month$0 pay-per-use at $0.05 a parse
Intro offerAccelerator, 5,000 credits for $200 on a first purchaseNone
Larger volumeEnterprise plan by quote, higher rate limits included$99 a month covers 5,000 parses, then $0.018 each
Add-onsNormalization, geocoding, OCR and LLM parsing billed on topNone offered, so one parse is one unit
ConcurrencySerial required for batches under 100,000 documentsNo documented serial requirement
ScansOCR is a priced add-onAccepted at the same price

Both shapes have a run size where they win. A credit plan wins when you need what the credits also buy: normalized skills and job titles against a taxonomy, matching, job posting parsing, a support relationship with a quote behind it. A per-parse API wins when the backlog is the whole job and the output is the fields the CV plainly states.

Who should stay on a credit platform for their bulk run:

If one of those is load-bearing, stop reading here: the cheaper per-document number below is the wrong number for you.

Running the backlog against a per-parse API

For the narrower job, the loop is short and the bill is easy to predict. A practical shape, in any language:

  1. Enumerate the archive into a queue table with a status column. Never drive the loop straight off a directory listing, because a crash then restarts from zero.
  2. Filter before you pay. Drop zero-byte files, duplicates by content hash, and anything over the size limit. On our API that limit is 20 MB per upload, which covers a photographed CV at flagship phone resolution.
  3. Pick a concurrency and hold it. Start at 4 and watch the error rate before raising it. Faster is worth nothing if a fraction of the calls come back refused and charged.
  4. Watch the quota headers rather than the clock. Our gateway returns the plan quota, the remaining count and the seconds until the window resets on every response. A loop that reads x-ratelimit-requests-remaining knows it is about to run out an hour before it does.
  5. Treat a 429 as a stop, not a retry. On our listing a 429 means the monthly quota is spent. Retrying spells the same answer more expensively. Wait for the reset or move up a plan.
  6. Record failures with their code. unreadable_file, not_a_resume and too_large are your archive telling you something true about itself, and they are usually worth fixing in the queue rather than in the loop.

A worked example at our published prices: a 40,000 CV backlog on the $99 plan uses the 5,000 included parses and then 35,000 at $0.018, which is $630 plus the $99, so $729 for the run. On pay-per-use at $0.05 the same backlog is $2,000. The plan is worth taking for a single large import and cancelling afterwards, and it is worth saying plainly that a job this size is where the monthly plan beats our own pay-per-use rate by a wide margin.

The mistakes that cost the most

Where to go next

If you are choosing the API rather than sizing the run, our CV parser API comparison covers what each option returns and what it charges. The plan-level detail behind the credit column above is in Textkernel pricing. If the archive is full of scans, OCR resume parser covers what changes when the CV is an image. And if you want to try one document before you write the loop, the free parser takes a file and returns the JSON with no signup.

All articles