Key takeaways
- Submission writes to someone else’s system under a real name, with no rollback.
- Write the idempotency record before the attempt, or a crash leaves you unable to tell.
- Pace per employer and per platform, not just globally.
- Half-success is the normal case; decide in advance what each partial state means.
- A circuit breaker on failure rate is the difference between stopping at three and at three hundred.
Why this half is different
Searching and scoring are read-only: get them wrong and you waste compute. Submission writes to somebody else’s system, under your user’s name, permanently. There is no rollback and no way to explain the mistake to the reviewer who will see it.
That single property should shape every design decision. Auto-apply is closer to a payments integration than to a scraper, and it deserves the same paranoia.
The asymmetry runs through everything. A failed search costs a retry; a wrong submission costs a candidate an opportunity at a company they may care about, and no amount of later correctness recovers it.
Idempotency is not optional
The failure that defines this domain: a submission times out, the job retries, and the employer receives two applications from the same person. It looks careless at best and automated at worst, and it is the single most damaging bug available.
Every application needs a key — candidate plus job, deduplicated across sources — checked before submission and recorded atomically with the attempt. If the process dies between submitting and recording, on restart you must be able to tell whether it went through, which usually means an idempotency record written *before* the attempt and updated after.
Deriving the key is where this gets subtle. The same role arrives through four sources with four URLs, so keying on the posting link produces four keys and defeats the whole mechanism — the key has to come from employer plus normalised role plus candidate.
- A unique key per candidate-job pair, resolved across duplicate postings
- Written before the attempt, not after, so a crash is recoverable
- A reconciliation pass for records stuck in "attempted, unknown"
- A hard cap per employer per day, whatever the queue says
| Failure | Cause | Control |
|---|---|---|
| Duplicate application | Retry after timeout | Idempotency key, written first |
| Silent non-submission | Missing confirmation | Positive verification required |
| Half-filled application | Conditional field appeared late | Completeness check before submit |
| Hundred bad applications | Form changed | Circuit breaker on failure rate |
| Blocked account | Submitting as fast as possible | Per-employer rate limit |
| Wrong role applied to | Deduplication collapsed two roles | Verify the title on the page |
Treat it as a queue with rate limits
Submitting in a loop as fast as the code allows is how accounts get blocked and how a bug turns into fifty bad applications before anyone notices. Applications belong in a queue with deliberate pacing.
Rate limit per employer and per applicant tracking system, not just globally. Twenty applications to one company in a minute is a pattern that gets noticed, and it harms the candidate more than any efficiency gain is worth.
Nothing about this workload benefits from speed. An application submitted twenty minutes later is worth the same as one submitted now, so pacing costs nothing real and removes an entire category of risk — which is an unusually good trade and one teams still argue about.
Partial failure is the normal case
A form submission has many ways to half-succeed: the CV uploads but the cover letter does not, a required field appears only after another is filled, the session expires mid-flow, a confirmation page never loads.
Decide in advance what each partial state means and record it honestly. The worst outcome is a system that reports success because it did not receive an error, leaving the candidate believing they applied when nothing arrived.
Require positive evidence of submission rather than an absence of errors. A confirmation element, a redirect to a known success page, or the application appearing in the portal’s own list — and anything short of that is recorded as unknown and resolved later, not as success.
The audit trail is the product
Store exactly what was sent: the resolved field values, the file that was uploaded, a screenshot of the final state, and the timestamp. Not a summary — the actual content.
Two reasons. The candidate will be asked about this application in an interview and needs to know what they claimed. And when something goes wrong, the only way to tell a tool bug from an employer form change is to see what was actually submitted.
Give the trail a retention policy of its own, because it holds a complete copy of someone’s application materials and a screenshot of a page containing their personal details. It deserves the same scoping and deletion guarantees as the primary record rather than becoming an unexamined archive.
Safeguards that earn their keep
Build a circuit breaker before you need one. If the failure rate across recent submissions crosses a threshold, stop everything and alert rather than continuing — because a form change usually breaks every submission the same way, and the difference between noticing at three and at three hundred is entirely how fast you stop.
Add a dry-run mode that performs every step except the final click and stores the result. It is the only way to test changes safely, and you will use it constantly.
A user-facing stop matters as much as an internal one. A candidate who realises something is wrong should be able to halt everything in one action without contacting support, and the same mechanism is what you will reach for during an incident.
Treat the caps as permanent rather than as defaults to be raised. A per-day, per-employer and per-week limit that survives a user asking for more is what keeps a bug proportional, and the request to remove it is exactly the moment it is doing its job.
Frequently asked questions
What is the most dangerous bug in an auto-apply tool?
Duplicate submissions. A retry after a timeout sends a second application to the same employer, which reads as careless or automated — and it is the easiest bug to introduce.
How do I make job submissions idempotent?
Key on candidate plus employer plus normalised role, not the posting URL. Write the record before attempting, update it after, and reconcile anything left unknown on restart.
What should an auto-apply tool store about each application?
The exact resolved field values, the uploaded file, a screenshot of the final state and a timestamp — not a summary. The candidate will be asked about it, and it is the only way to diagnose failures.
How do I stop one bug becoming a hundred bad applications?
A circuit breaker on failure rate across recent submissions. A form change usually breaks every attempt the same way, so stopping automatically at three instead of three hundred is the entire safeguard.
How do I know an application was actually submitted?
Positive evidence only — a confirmation element, a known success redirect, or the entry appearing in the portal list. An absent error is not a confirmation.
Does pacing cost anything?
Nothing real. An application twenty minutes later is worth the same, so per-employer rate limits remove a category of risk at no cost to the outcome.
Check this against your own resume
Scan your CV against a real job description, or build a parse-safe one from scratch. Your first scan costs nothing.
