ImportKit's limits
Every limit ImportKit imposes, with the reason for it. Where a limit exists because of a real trade-off, the trade-off is named rather than hidden.
Shopify separately caps how many definitions you can have, how large a metafield value can be, and how many items fit in a list. Those are on Shopify's limits — along with an honest account of which ones ImportKit checks before your import runs, and which only surface when Shopify rejects a row.
File size and rows
| Import | Pre-import check | |
|---|---|---|
| Maximum rows | 1,000,000 | 50,000 |
| Maximum file size | 50 GB | 32 MB |
Both the file size limit and the number of files per import can be raised — email support@importkit.app with a rough idea of the volume and what you're importing. The 50 GB figure is a sensible default rather than a technical ceiling.
Two very different budgets, because two very different jobs. The import path streams: the browser parses the file row by row without holding it, the upload is consumed as a byte stream, and rows are written to storage in batches. Memory stays flat whatever the file size, so those caps are guard rails against absurd input rather than a memory ceiling.
Very large uploads don't pass through the app server at all — they go to storage as a resumable multipart upload, which is why the ceiling can be this high. Direct in-browser posts are capped much lower (25 MB) because they have to survive proxy body limits; the app picks the right path for you based on file size.
The pre-import check can't stream and never will. It reports a verdict for every row, detects duplicates across the whole file, and totals the credits required — all three are whole-file questions. So it holds the file in memory and has a much lower cap.
Over the check's limit
You'll see a “This file is too large to preview” notice, and the import is still allowed. You lose the preview, not the safety: every row is still checked against your store as it's imported, and your conflict mode is applied per row identically.
If you'd rather keep the preview, split the file into parts under 50,000 rows. Rows are independent, so splitting is always safe.
Over the import's limit
The upload is refused, with a message telling you to split the file — or to ask us for a higher limit:
This file is over the 50.0 GB upload limit. Split it into smaller
files and import them one at a time, or contact support to request a
higher limit.
The size check runs as bytes arrive, so an oversized file is never pulled into memory in the first place.
The real limit on a very large import is time
Rows go to Shopify one at a time. A million-row file is allowed, but it will take a long while — the constraint is the Admin API, not ImportKit. For anything at that scale, split it and run the parts in sequence so you get checks, clearer results and manageable retries.
File format
| Constraint | Value |
|---|---|
| Accepted extension | .csv only |
| Delimiter | Comma |
| Encoding | UTF-8 |
| Header row | Must be the first row |
| Empty rows | Skipped automatically |
| Blank cells | Skipped, not written — can't clear an existing value |
| Cell values | Trimmed of leading and trailing whitespace |
Throughput and retries
| Behaviour | |
|---|---|
| Concurrency | Rows are processed strictly one at a time. |
| Transient failures | Up to 3 attempts per row on a rate limit or server error, backing off 1 second then 2. |
| Other failures | Not retried. A validation error won't become valid on a second attempt. |
| Cancellation | Checked every 5 rows, so a cancel takes effect within a handful of rows rather than instantly. |
| Execution | An in-process background job on our servers, not a durable external queue. |
| Interrupted jobs | A job stuck for more than 15 minutes is marked interrupted and can be resumed with Retry. |
Serial processing is a deliberate choice, not a missing feature. Shopify's Admin API throttles hard enough that parallel writes get rejected and end up slower, and one-at-a-time keeps row ordering predictable and every row's outcome unambiguous.
Cancellation is polled rather than checked per row because checking per row would add a database query per row, for every import, to serve a feature used occasionally.
Read pagination
ImportKit reads your store to detect collisions and resolve references. Most of those reads are fully paginated; two aren't, and one of those is worth knowing about.
| Read | Limit |
|---|---|
| Metafield definitions (collision detection) | All pages, 250 per page |
| Metaobject definitions | All pages, 250 per page |
| Metaobject entry handles (collision detection) | All pages, 250 per page |
| Blogs offered as import targets | First 50 |
| Article metafield definitions offered for mapping | First 100 |
| Metaobject entries used to resolve a reference by handle or name | First 250 only, not paginated |
Referencing a metaobject entry by handle or display name works only for the first 250
entries of the referenced type. Beyond that, use a full
gid://shopify/Metaobject/… value, which needs no lookup. Collision detection for
entries is fully paginated, so create-versus-update decisions are unaffected.
More →
Data rules enforced before any API call
| Rule | Detail |
|---|---|
| Metafield key | 2–64 characters, letters, numbers, hyphens, underscores |
| Namespace | 3–255 characters, same character set, defaults to custom |
| Reserved namespaces | $app, app--, shopify-- and shopify are rejected — a CSV import creates merchant-owned definitions |
| Metaobject type | 3–255 characters, same character set. An $app: prefix is stripped with a warning |
| Metaobject entry handle | Letters, numbers, hyphens, underscores |
| Metafield types recognised | 113, including every list.* variant. An unrecognised type is a warning, not an error, so Shopify stays the authority |
| Metafield definition type | Immutable. A row whose type differs from the existing definition is a conflict in every conflict mode |
| Metaobject definition fields | Field types immutable. Update mode adds new fields only and warns on any type mismatch |
Owner types
Metafield definitions: 13 owner types. Metafield values: 7 owner types. Customers, orders, draft orders, companies and gift cards are unreachable by design.
Limits that aren't ours
Everything above is a constraint ImportKit imposes. Shopify imposes its own, and they bite differently — a 64 KB metafield value size, 128 items per list, 40 fields per metaobject definition, 256 definitions per owner type. Most are measured by the pre-import check; the whole-store ceilings still surface as failed rows.
That's worth understanding before a large import: Shopify's limits →
Results and history
| Behaviour | |
|---|---|
| Row results paging | 200 rows per page |
| Large job detail view | Shows the first portion of rows with a banner saying how many of how many |
| Errors download | import-<jobId>-errors.csv with Row,Title,Error |
| History retention | Kept while the app is installed; deleted on shop redaction after uninstall, or on request |
Billing
| Behaviour | |
|---|---|
| Cost | 1 credit per row written |
| Not charged | Skipped, conflicting, errored and uncertain rows; the pre-import check; uploads; mapping; templates |
| Model | Prepaid one-time purchases. No subscription, no expiry |
| Enforcement | One credit reserved before each row is written, released if the row isn't written |
| Out of credits | The import stops cleanly mid-file and is resumable with Retry |
Things ImportKit deliberately doesn't do
- No deletion and no rollback. Nothing is ever deleted. An import can create or update; undoing one is manual work in the Shopify admin. This is why the pre-import check exists.
- No protected customer data. Customer, order, company and draft order metafields are unreachable by design.
- No resource creation for metafield values. The product, page or collection has to exist already.
- Blog articles are always published, and there's no draft or status column.
- Blog articles have no duplicate detection and no pre-import check, because articles aren't uniquely keyed.
- Blank cells can't clear a value. They're skipped rather than written.
- Metaobject reference lists on articles are comma-separated only; a newline-separated cell won't resolve there.
-
Files aren't uploaded. A
file_referencemust point at something already in your Shopify files. - No scheduled or recurring imports. Every import is started by you.
The reasoning behind each of these →