How to import your stockists from a CSV (and what a re-upload does to your pins)
October 2026
You have a spreadsheet of stockists. Maybe it is a Faire export, a dealer list from your sales rep, or a tab you have been adding to for three years. This is how to get it onto a store locator map in one upload, and how to upload it again later without wrecking the pins you have already fixed.
The steps below use Nearmio's importer, but the checklist ahead of the upload applies to any CSV import.
Before you start
Only one column is required: name. But a name alone does not place a pin. Every row also needs one of these:
latandlng(decimal degrees), oraddress_line1,cityandcountry, orpostcodeandcountry.
Country is not guessed. A row with a street and a city but no country, and no coordinates, comes back as invalid. If your whole sheet leaves country out because everyone knew it was the US, add the column and fill it down before you upload.
Get the exact layout by downloading the template from the import page (Locations, then Import from CSV, then Download the template). It has 14 columns and two sample rows:
name, address_line1, city, region, postcode, country, lat, lng, phone, hours, website, google_listing_url, tags, external_id
Three more things to know before you open the file:
- Tags go in one column, separated with a pipe, not a comma:
flagship|authorized-dealer. - Unknown columns are ignored, so you can leave "Last order date" and the like in the file.
- Add an
external_idcolumn now, even if you think you will never need it. The re-upload section explains why.
Column names do not have to match
Headers are trimmed, lower-cased, and spaces become underscores before matching, so Store Name, store_name and STORE NAME are the same column. Common alternatives work without renaming:
| You have | Nearmio reads it as |
|---|---|
| zip, zipcode, postal_code | postcode |
| state, province, county | region |
| store, store_name, location, title | name |
| address, street | address_line1 |
| latitude / longitude, lon, long | lat / lng |
| id, store_id, ref | external_id |
If two columns map to the same field, the first one wins. The full list of accepted headers is in the help center.
Read the preview before you commit
Drop the file on the import page and choose Preview import. Nothing is imported yet. You get a count and a row-by-row table, and every row is one of five things:
- New: a location that does not exist yet.
- Update: matches a location you already have (more on matching below).
- Duplicate in file: the same row appears earlier in this file. Only the first is used.
- Over plan limit: it would be a new location, but your plan is full. The Free plan holds 5. These rows are skipped, not silently dropped, and rows that only update existing locations do not count against the limit.
- Invalid: the table's note column says why.
The two numbers worth a glance are "updates" and "new". If you changed six rows and the preview says 200 new, stop. The rows did not match what you already have, and committing would add 200 duplicates. That is the most useful thing the preview does.
The importer also stops before the preview if the file is empty, has no name column, is larger than 1 MB, or has more than 2,000 rows. For a bigger list, split it into several files.
Commit, and what happens next
Choose Commit import. It runs in the background and you get a progress screen. Rows that already have coordinates are used as they are. Rows with only an address are looked up and pinned, which is why a long file takes a while. You can leave the page and come back.
When it finishes, download the results file. It has one line per row of your CSV (line, name, status, reason), and the status is one of placed, needs_fix, duplicate, skipped_limit or invalid. A needs_fix row was imported but its address could not be placed, so it is left off the storefront map until you fix the pin in the app.
Uploading the same spreadsheet again
Stockist lists change, so you will upload the file again. Here is exactly what happens.
How a row is matched. By external_id if the row has one. Otherwise by address: street, city, region, postcode and country, compared after trimming and ignoring case. A row with an external_id that no location has yet is matched by address instead, if the location at that address has no id; the id is then saved on it. A match is an Update. No match is New.
What an update changes. Cells with a value overwrite what is stored. Empty cells are left alone, so a blank phone cell does not wipe a phone number you typed in the app. To clear a field, edit the location by hand. If a row has a tags cell, it replaces the tags.
What happens to your pins. If you fixed a pin by dragging it, it stays put as long as the row's address is unchanged and the row has no lat or lng. Two things move it:
- Coordinates in the file always win. If the row has
latandlng, the pin goes there. - If the address changed, the new address is looked up again and the pin is placed from it.
So if your export tool fills in lat and lng on every row, a re-upload will put every pin back where the file says, including the ones you moved. If you have hand-fixed pins, upload without coordinates or delete those two columns from the file. How to find and fix a wrong pin in the first place: my store locator pin is in the wrong place.
What it never does. An import never deletes. A store that is gone from your spreadsheet stays on the map until you delete it in the app.
Where it goes wrong. Matching by address breaks when the address is written differently. "St" for "Street", a postcode with a space added, or a region that was blank last time and filled in this time all produce a different address, so the row shows as New. Matching by external_id is not fooled by that, which is why to add it early. Two specifics:
- If you imported without an
external_idcolumn and add one later, the rows still match by address on that upload and show as Update, and the id is saved on each location. The next upload matches by id. The exception is a row whose address belongs to a location that already has a different id: it shows as New, and the old location keeps its id and pin. Read the preview before you commit. - A row with coordinates but no address cannot be matched by address. Without an
external_idit comes in as New every time you upload. If you add anexternal_idto rows you imported that way, they come in as New once more, because the earlier locations have no id and no address to match; after that, the id matches them.
Common invalid rows
- Missing name: the name cell is empty.
- Needs coordinates or a full address: a street and city with no country, a city with no street, or a postcode with no country.
- Malformed or incomplete coordinates: a
latwithout alng, a value outside -90 to 90 (lat) or -180 to 180 (lng), or 0 and 0. If the same row has a good address, the bad coordinates are ignored and the address is used. For these reasons, the row is only invalid when there is no address to fall back on. - Coordinate cell with extra characters: something like
39.78abcor1,234.5inlatorlng. The row is invalid even if it has a good address, and the note names the cell. A decimal comma such as"39,78"is read as 39.78 (keep it in quotes).
When to use Sidekick instead
If your spreadsheet is in a completely different layout, hand it to Shopify Sidekick before you upload. It maps your columns to Nearmio's, lists what each row is missing with row numbers, and can write the rows out in the template layout. It does not import or place pins; you still upload the file and read the preview. See Import any stockist spreadsheet with Shopify Sidekick.
Starting from Faire? Your Faire retailer list is a ready-made CSV. And once the map is live, how to keep your stockist list up to date covers the upkeep.
Free for up to 5 locations, no Google API key to set up.