Problem & solution

Start List Matching in Marathon Photos — Bibs to Names

Reading the bib number is only half of the job. A folder full of photos tagged "1247" is searchable by you and by nobody else — the runner looking for herself in your gallery types her name, the club looking for its members types the club name, and the organizer wants the results list and the photo archive to speak the same language. All of that lives in one file the organizer already has: the start list. This guide is about the join between the two — how a start-list CSV turns numbers you read into names you can deliver on, what a road-race start list actually looks like when it lands in your inbox two days before the event, and the specific ways that file can be wrong in a way that quietly puts the wrong name on a photo.

Photos tagged only with numbers are effectively unfindable by the people most likely to buy them. Runners search their own name, clubs search their club, and press desks need a name in the caption — so an archive keyed to bare numbers pushes the lookup work back onto you, one query at a time, in the days right after the race when interest in the gallery is at its highest.

Understanding the problem

Start-list integration is the step that joins two datasets: the bib numbers detected in your photos, and the participant list supplied by the event. The join key is the number, and everything you actually want to deliver — the runner's name, their category, their club or team — is carried by the list rather than by the photo. Done once per event, it converts the whole take from number-tagged to name-tagged.

For mass-participation road running, the archive is the product. Photo sales, gallery search, club deliveries, and press captions all resolve through a name or a club, not through a bare integer. And because the list is a single import, the difference between a properly matched event and an unmatched one is not a per-photo cost — it is the difference between a gallery that people can search and one they can't.

In this sport specifically

Road running start lists come from timing and registration platforms, and they arrive in whatever shape that platform exports: names split into two columns or joined into one, categories written as age bands or as codes, club fields left empty for unaffiliated runners, accented characters mangled by the export encoding. Fields are usually finalized late, because race-week registration, bib reassignments, and no-shows are normal in a field of tens of thousands. And unlike a motorsport entry list, a marathon list is a list of private individuals — the names in it are personal data you are choosing to write into files you distribute.

Where it shows up

Traditional approaches, and why they fall short

Keeping the start list open in a spreadsheet and looking up numbers by hand as you caption

Scales linearly with the take — every distinct runner in the gallery is one more manual lookup · Accurate while you are fresh; transposition errors creep in as the session runs long

It puts the join at the slowest possible point — after the shoot, at one photo at a time, on the night when the gallery is most valuable. The runners who never get looked up are simply never findable by name.

Delivering the gallery keyed on bib numbers only, and letting buyers search by number

No tagging cost, but a permanent support cost · Exactly as accurate as the numbers you read — no name-level errors, because there are no names

It works only for the buyer who remembers their number and knows to search by it. Everyone searching a name, a club, or a category — which is most people, and every press desk — finds nothing, and the questions come back to you.

Matching after the fact against published results by finish time

Low per photo once set up, but depends on synchronized clocks and reliable position notes · Reasonable at a sparse finish line; unreliable mid-course and in crowded frames

It infers identity from where a runner should have been rather than reading who is in the frame. In a mass field, several runners share any given second at any given point on the course, so the inference is confidently wrong rather than blank — and there is nothing in the output that tells you which is which.

How RaceTagger handles it

RaceTagger reads the bib numbers visible in each photo with a vision model that is looking for a race bib rather than transcribing any text it finds, then matches each read against the participant list you imported for the event. The list is a CSV: a number column plus the tag text to attach, with optional name, category, and team columns — and if you supply name, category, and team without a tag column, it composes the tag from them. Extra columns you include are carried through as additional fields rather than dropped. A number that matches no row in your list doesn't resolve to a participant, and a read that doesn't resolve cleanly is marked as needing review — with the closest candidates from your list surfaced for you to confirm — rather than being accepted as a settled match.

Key advantage

Identity comes from the number that is actually in the frame, checked against a real list — not inferred from timestamps or finish position. That means an uncertain read arrives marked as uncertain, with candidates you can choose between, rather than as a plausible name silently attached to the wrong runner.

Good conditions
A flat, unobstructed bib facing the camera, with a number that exists in the imported list — reads and matches cleanly, and the name, category, and club from your list carry into the metadata
Challenging
Bibs folded at the edge, partly covered by a hydration belt or a jacket, or angled away — often still readable, but more of these come back marked for review, and a partial read that matches no row in the list doesn't resolve to a participant
Worst case
The bib is not visible in the frame at all, or the event uses name-only bibs — there is no number to join on, so the photo isn't matched to anyone rather than being given a guessed identity

Import the event's list once, then batch-process the folder. RaceTagger handles JPEG and RAW — reading RAW via the embedded preview — so you don't convert a day's shooting first, and matches are written as EXIF/XMP/IPTC metadata — embedded in the file for JPEGs, and written to an XMP sidecar alongside RAW files rather than into the RAW itself — with uncertain reads carrying a review marker rather than being delivered as a settled identity. That means the names and clubs travel with the photos into Photo Mechanic, Lightroom, or Capture One and into whatever gallery platform you deliver through, instead of living in a spreadsheet alongside them. Running is a dedicated sport profile in the app; identification there is bib-driven, not face-driven, so a runner is only identified when their number is in the frame.

Manual vs OCR vs AI vision

MetricManualBasic OCRRaceTagger
How the number becomes a nameYou look it up in the spreadsheet, one runner at a timeIt doesn't — OCR returns digits and stops thereRead numbers are matched against the imported list in the same pass
A number that isn't in the listYou notice it, and decide what to doWritten as-is, with no way to know it was never a valid bibNo name written — a number with no match in your list produces nothing; an ambiguous read is marked for review with candidates to confirm
Sponsor text and course signage in the frameIgnored — you know what a bib looks likeFrequently returned as though it were the bib numberLooks for a race bib, and a read matching no list entry doesn't get accepted
Where the names end upIn your captions, or in a sidecar spreadsheetWherever you paste them afterwardWritten into the files as EXIF/XMP/IPTC, so they travel into Photo Mechanic, Lightroom, or Capture One
Cost modelYour hours, on the night the gallery matters mostCompute only, plus the manual pass to find wrong and missing readsCredits — 1 credit per photo analyzed

Practical tips

  1. 1

    Ask for the start list after the race-week cutoff, not the one that's already in your inbox

    Late entries, transfers, and bib reassignments are settled close to the start. Every match you make inherits the trust of the list, so the freshest export the organizer can send — ideally the same file the timing system ran on — is worth more than any amount of care later.

  2. 2

    Import one list per race, and keep folders per race, when the day has several distances

    If the marathon, the half, and the 10K reuse numbers, no tool can tell them apart from the digits on the bib alone. Splitting the day by race — separate folders, separate lists — removes the ambiguity structurally instead of hoping the match resolves it.

  3. 3

    Normalize the name column before you import, not after you deliver

    Exports split names across columns, use inconsistent capitalization, or mangle accented characters. Five minutes in a spreadsheet fixing the column you actually want written into the metadata is cheaper than correcting the name on every file that carries it.

  4. 4

    Decide deliberately what personal data goes into the files you hand out

    A start list is a list of private individuals, and metadata written into a JPEG travels with that file anywhere it is forwarded. Import the columns your delivery genuinely needs — a name and a club for a searchable gallery — rather than the whole registration export with contact details and age in it.

  5. 5

    Spot-check the match on a handful of runners you can identify yourself before processing the whole take

    Run a small folder first and confirm that number, name, and club line up on runners you recognize. A column mapped to the wrong field is invisible in the abstract and obvious on five photos — and catching it there saves rewriting metadata across the entire event.

The takeaway

The number in the photo and the name in the start list are two halves of the same record, and the join between them is a one-time import rather than a per-photo chore. What makes it work is the discipline around the list: get the version that was actually used on race day, keep separate distances separate, and import only the personal data your delivery needs. What makes it trustworthy is what happens when the join fails — a number that matches nothing gets no name at all, and an uncertain read comes back marked for your eye — instead of either one quietly becoming somebody else's name.

Turn a folder of bib numbers into a gallery people can search by name

500 free credits when you sign up — 1 credit per photo. Import your event's start list, run a folder, and see the names land in the metadata.

Try it free →

Questions photographers ask

What does the start-list CSV need to contain?

At minimum a column with the bib number and a column with the tag text to attach to it. If you supply name, category, and team columns instead of a prepared tag, RaceTagger composes the tag from those, and any extra columns you include are carried through as additional fields rather than discarded.

The organizer sent me the list in their timing platform's own format. Is that a problem?

Usually not — it is a spreadsheet export, and the work is renaming and tidying columns rather than converting formats. The part worth doing carefully is deciding which column becomes the text written into your photos, especially when the export splits names across separate first-name and last-name fields.

What happens when a bib number isn't in the list I imported?

There are two different outcomes worth separating. If the number simply isn't in your list, the photo isn't matched to anyone — no name, and nothing to choose between. If the read itself is ambiguous or uncertain, that's the other case: it's marked for review with the nearest candidates from your list for you to confirm. Either way, nothing is delivered as a settled identity on a guess, and a number that corresponds to no entry is exactly the signal you want — it means either a misread digit or a runner who registered after your export.

My event runs a marathon, a half, and a 10K with overlapping numbers. Can it tell them apart?

Not from the photo alone — the digits on the bib are the same digits, and nothing else in the frame reliably resolves which race a runner is in. Handle it structurally: keep the races in separate folders and match each against its own list, rather than one merged list for the day.

Can it identify runners by face when the bib is hidden?

No. Running is set up as a bib-driven profile with face recognition off — identification comes from the number in the frame, so a runner whose bib is covered by a jacket, turned away, or out of shot isn't matched to anyone. We would rather leave that photo unidentified than attach a name we inferred.

Do the runner names actually end up inside the image files?

For JPEGs, yes: matches are written into the file as EXIF/XMP/IPTC metadata, so the name, category, and club from your list travel with the photo into your editor and your delivery platform, while uncertain reads carry a review marker rather than being recorded as settled. RAW files work differently — nothing is written inside the RAW itself. The metadata goes into an XMP sidecar file next to it, which Photo Mechanic, Lightroom, and Capture One read, but which has to travel with the RAW: forward a .NEF or .CR3 on its own and the names stay behind. For the JPEGs you deliver, the data does travel to anyone the file is forwarded to, which is worth keeping in mind when you decide which columns to import.

Keep reading

← All guides