What You Actually Lose When You Export Test Cases from TestRail (CSV vs XML vs API)
Applies to TestRail Cloud and TestRail Server, version 8.x and later.
A TestRail export gives you your test library and almost none of your test evidence. The three file formats in the UI (Excel, CSV and XML) carry test cases, sections, steps and custom field values, and they carry no attachments, no images, no test runs, no results, no comments and no change history. TestRail states the run-and-result part plainly in its own migration documentation: "Test runs and results are tied to specific configurations and historical data that aren't supported through CSV/Excel or XML imports." Everything beyond the case library has to come out through the API, one entity at a time, and a few things (bulk attachment download, per-case audit trails in a file) have no export path at all. If you are planning a move off TestRail, that split between library and evidence is the whole story, and it is worth knowing before you promise anyone a lossless migration.
The takeaway in 30 seconds
- The UI export offers three formats: Excel, CSV and XML. XML has no configuration options and is the only one TestRail can re-import into itself without manual mapping.
- No file format contains attachments or images. Two competing test management vendors, Testmo and Testiny, state this explicitly in their own import documentation.
- There is no UI export for test runs or results anywhere in TestRail. The only documented route is the API:
get_runsfollowed byget_results_for_run, returning JSON. - Custom field values are exported. Custom field definitions are not, and in XML the values for dropdowns, multi-selects, milestones and users are numeric IDs. Only the API can turn those IDs back into labels, through
get_case_fields. - The API is capped at 250 records per request, with a documented maximum of 250. TestRail Cloud adds a rate limit of 180 requests per minute on Professional and 300 on Enterprise; TestRail Server has no built-in limit.
- Per-case change history exists only through
get_history_for_case, and only on TestRail 6.5.4 or later. No file format documents it. - Plan for a partial migration and decide deliberately what you are leaving behind. Every honest vendor migration path does the same thing, whether or not they say so on the marketing page.
What TestRail actually gives you to export
A TestRail export is the process of pulling test cases, runs, or results out of TestRail via its UI (CSV/XML) or REST API for migration or backup.
There are three separate mechanisms, and they are not alternatives to each other. They carry different data, and a real migration usually uses two of them.
The three file formats in the UI
From the Test Cases page of a project, the export icon opens a dialog with Excel, CSV and XML. Excel and CSV let you choose which sections to include (hold Ctrl or Cmd to select several) and which columns to include, and both offer a checkbox called "Include separated steps on separate rows". Excel adds one more option, "Include separator hint for maximum Excel compatibility", which TestRail recommends leaving checked.
XML has no options at all. You click it and the file downloads. TestRail describes the format as "most useful for further processing or integration with other tools" and notes that "exporting as XML also allows seamlessly importing back to TestRail, maintaining all the original sections and test cases structure". That round trip is real: TestRail's XML import documentation confirms that "the import and export functions use the same file format", with the export carrying one extra top-level element that the importer ignores.
What the export documentation does not mention is instructive. It says nothing about attachments, nothing about images, nothing about results, nothing about revision history, and nothing about how dropdown values are represented. The page lists formats and options and stops.
The API
The API is the only route that reaches most of your data. It runs over index.php?/api/v2/, returns JSON, and has to be switched on under Administration > Site Settings > API before anything works. The endpoints that matter for a migration are roughly these:
get_suites/{project_id} suites
get_sections/{project_id} section tree
get_cases/{project_id} the case library
get_case_fields custom field definitions and option lists
get_shared_step(s) shared step bodies (TestRail 7.0+)
get_history_for_case/{case_id} per-case change log (TestRail 6.5.4+)
get_attachments_for_case/{id} attachment metadata only
get_attachment/{attachment_id} the actual file
get_milestones/{project_id} milestones and sub-milestones
get_configs/{project_id} configuration groups
get_plans/{project_id} test plans
get_runs/{project_id} test runs
get_results_for_run/{run_id} results for a run
get_attachments_for_test/{id} attachments on results
get_users users (admin-gated, see below)
Note how many separate calls that is for one project. Attachments in particular need two calls per file, because the metadata endpoint and the binary endpoint are different things.
The database export
There is a third path most people miss. TestRail's Data Storage and Management documentation describes an Exports tab used "to schedule backups of your TestRail database and uploaded files, for local installation, migration, or archiving", configurable to include reports, attachments and a chosen SQL version. It is the instrument TestRail points at when a customer moves from Cloud to Server.
Two caveats before you count on it. It produces a database dump plus files, not a portable interchange format, so unless the destination is another TestRail instance you are writing your own extraction against TestRail's schema. And the documentation on plan availability is not consistent: the Data Management article describes it for Cloud administrators from TestRail 7.1, while the separate article on configurable backup time and restoration calls the Backups feature Enterprise-only. If this path matters to your plan, confirm your own entitlement with TestRail rather than trusting either page.
Route by route, entity by entity
This is the table nobody publishes. Every cell below traces to TestRail's own documentation or to a named vendor's import documentation, and where a behaviour is genuinely undocumented it says so rather than guessing.
| What you want | CSV / Excel | XML | API |
|---|---|---|---|
| Test cases and titles | Yes, selectable columns | Yes | Yes, get_cases |
| Section and sub-section hierarchy | Only if you include the section hierarchy column | Yes, nested natively | Yes, get_sections |
| Separated steps and expected results | One step per row, first row also holds the case fields | Structured, with index, content and expected | Structured array with content, expected and shared step reference |
| Shared step bodies | Undocumented | Undocumented | Separate object, get_shared_steps, TestRail 7.0+ |
| Custom field values | Yes, if the column is selected | Yes, inside the custom element | Yes, as custom_* fields |
| Custom field definitions and labels | No | No | Yes, get_case_fields |
| Dropdown and multi-select values | Undocumented | Numeric IDs | Numeric IDs, decodable via get_case_fields |
| References and issue IDs | Undocumented | Yes, as a plain string | Yes, the refs field |
| Attachment files | No | No | Yes, two calls per file |
| Inline images inside step or description text | No file content | No file content | Yes, same two-call path |
| Test runs | No | No | Yes, get_runs |
| Results, who ran what and when | No | No | Yes, get_results_for_run |
| Result comments and step results | No | No | Yes, in the result payload |
| Attachments on results | No | No | Yes, but via a separate endpoint, not in the result payload |
| Test plans | No | No | Yes |
| Milestones | Value only, if the column is selected | Value or numeric ID | Full objects including sub-milestones |
| Configurations | No | No | Yes, groups with nested configurations |
| Users and assignment | No | User fields as numeric IDs | Yes, with permission limits |
| Per-case change history | Not documented | Not documented | Yes, TestRail 6.5.4+ |
| Re-import into TestRail | Yes, with manual column and value mapping | Yes, same file format | Yes, through the write endpoints |
Attachments and inline images
This is the loss people discover last and feel most. TestRail accepts files up to 256 MB across a long list of types, attachable to cases, runs, plans, milestones and results, and it keeps them in a library that can be reused across projects. None of that comes out in a file export.
Two competing vendors say so in their own documentation, which is a stronger source than any single one of them alone. Testmo, describing its TestRail importer, writes that "TestRail does not include any attachments or images in the XML file". Testiny's TestRail migration guide says that "since TestRail does not include any images or files in its XML exports, images and attachments cannot be migrated this way", and its landing page adds that XML "can lead to inevitable data loss, such as formatting, images, and attachments". Xray's importer documentation lists "Test case attachments" among the unsupported items outright. Qase states that its UI import "does not migrate attachments, and custom field data".
So attachments only move through the API, and even there the path is awkward. get_case and get_cases return no attachment data at all, so you first call get_attachments_for_case to get metadata (id, name, size, created date, owner) and then get_attachment per file to receive the binary in the response body. On results it is worse: the documented get_results payload has no attachment field, so result evidence has to be collected through get_attachments_for_test. And there is no bulk download anywhere. A request on TestRail's community forum asks exactly for it ("I need to download many of them and to go one by one is a time consuming and onerous process", with a follow-up asking to select a group of cases and have the files land in folders) and carries no official answer.
Inline images add one more wrinkle. TestRail stores them inside the text of a field as markup pointing back at the instance, in the shape . The binary is not in the export, and the pointer is relative to the TestRail instance you are leaving, so whatever survives in the text is at best a reference to a server your team is about to stop paying for. Two practical consequences: budget for re-uploading images by hand, and do it before you cancel the subscription, not after.
QMetry's migration tool documentation is worth reading here even if you have no interest in QMetry, because it enumerates the holes more honestly than anyone: attachments over 25 MB on cloud or 10 MB on server are skipped, attachments inside comments do not come across, inline images in descriptions do not, and milestone attachments and references do not. It also names an upstream gap plainly, that "TestRail does not provide API to get attachments directly linked to Test suite". A separate signal in the same direction: testomat.io's open-source TestRail migration script downloads attachments using a browser session cookie rather than the API key, which is not what you do when the public API covers the job.
Steps, separated steps and shared steps
TestRail's separated-steps template stores each step as a structured pair of content and expected result. Each format flattens that differently, and the differences decide how much cleanup you do afterwards.
In CSV and Excel, a case with five steps becomes five rows. The first row carries all the standard case fields plus step one; the remaining four rows carry only their step and expected result. When importing such a file, TestRail asks you to nominate the column that "detects the start of a new test case", which tells you how fragile the arrangement is: if the identifying column is not genuinely unique per case, the rows regroup wrongly and you get a mess that looks plausible. This is also why TestCollab's documentation instructs users to enable "Include separated steps on separate rows" and to include the section hierarchy column before exporting, and why Xray's route goes XML first, then a conversion script, then a CSV bulk import.
In XML, steps are proper elements with an index, content and expected result, which is why every vendor that supports both formats recommends XML. In the API, steps arrive as a structured array with content, expected and a shared step reference.
Shared steps are the trap. They exist as a separate object from TestRail 7.0, with their own endpoints, and a case references one by ID rather than containing its body. TestRail does not document what happens to shared steps in a CSV or XML export, so if your library leans on them, test one exported case before you plan around the result. Fetch the shared steps through get_shared_steps and expand them yourself if the export turns out to carry only references.
Custom fields: values travel, definitions do not
Every route exports custom field values. No file format exports what those fields are.
The consequence shows up immediately in XML. TestRail's import schema documents how each type is represented, and four of them are numeric IDs: a dropdown appears as an id element, a multi-select as a list of id elements, a milestone as an id, a user as an id. Read the file without the instance beside you and you have a case whose severity is "3". Nothing in the file says what 3 means.
The API is the only thing that closes that gap. get_case_fields returns each field's system name, label, type, display order and, for dropdowns and multi-selects, the option list itself as newline-separated value and label pairs. That is your decode table. Pull it first, before the cases, and keep it: it is the difference between a clean import and a week of guessing.
CSV sits in an odd middle position that TestRail does not document. Its export column list is not published, and there is no documentation of whether dropdown columns emit labels or IDs. What is documented is the import side, where TestRail shows "the values present in the CSV on the left, and a series of menus allowing you to select the relevant field values", so the file plainly carries strings that TestRail itself cannot resolve automatically. Treat CSV custom field fidelity as something to verify on your own data rather than something to assume.
Two vendors have publicly given up on mapping custom fields cleanly, which tells you how hard this is in practice. aqua cloud's import documentation says field mapping is not supported and that "enabled fields in the test cases are added to the description of a test case to ensure you don't lose important data", which is a polite way of saying your structured fields become prose. testomat.io lists "Limited custom field support" as a CSV limitation.
References and issue links
References survive as text and stop being links. TestRail's XML carries a references element holding a comma-separated string such as REQ-1, REQ-2, and the API exposes the same content in refs. What does not travel is the mechanism that made those strings clickable. Testmo puts the technical reason well: "issue IDs from TestRail's references field cannot be directly imported as issues as they miss the required meta data", and its workaround is to park them in a temporary string field.
A practitioner account from a team that moved off TestRail names the same loss in less abstract terms. Writing on dev.to about migrating a QA team's repository to Qase, Sam E. Lawrence notes: "Sadly, we lost the TestRail feature that allows you to set a baseUrl scheme for these references, allowing one click back to the related ticket." The same write-up flags two other things worth knowing before you start, that "there will be some special fields that don't come over" and that the import "struggles with some special characters in the Description/Name field, so check over your tests for any garbled names after import".
If requirement traceability matters to you, rebuilding it on the far side is a real work item rather than a side effect of the import. Our guide to building a requirements traceability matrix that stays accurate covers what that looks like when the links are being created rather than migrated.
Execution history: the part that does not come out at all
Here is the fact that deserves its own heading. TestRail has no UI export for test runs or results. Not a limited one, not a lossy one. The documented way to get results out is an API sequence, described in TestRail's own article on exporting test results as "exporting the test results from one or more test runs or plans in a structured json format" using get_runs/{project_id} and then get_results_for_run/{run_id}.
What the result payload contains is decent: assigned user, comment, creator, timestamp, per-step results, linked defects, elapsed time, status and test ID. What it does not contain is attachments, which live behind their own endpoint. And what no endpoint gives you is a single "give me everything" call, so a full history extraction is a nested crawl: every run in the project, then every result in every run, at 250 records per page.
The industry has quietly standardised around losing this. Xray lists "Test Runs" as unsupported. testomat.io's built-in importer has a section headed "What doesn't transfer (yet)" naming test run history and results, comments and discussion threads, milestones and test plans. aqua cloud, Testmo, Testiny and TestCollab do not mention runs or results in their TestRail documentation at all, which for the file-only importers among them is structurally honest, since the format contains no execution data to import. Zephyr's migration mapping table is the most revealing of the group: it maps TestRail execution statuses to Zephyr execution statuses, which is the status vocabulary, and never claims the executions themselves.
Where vendors do claim history, read the small print. BrowserStack's Quick Import documentation states that "only test runs and milestones from the past year will be imported, while test cases have no time restrictions", and separately that "only TestRail Cloud versions are supported". Its marketing page for the same product promises to "ensure zero data loss". Both statements are from the same vendor, and the documentation is the one you should plan against. QMetry's tool claims runs and results but excludes "Dashboards, To Do, Reports of Projects, Overview, Activities, Graphs, History logs" and does not migrate TestRail users at all.
The honest summary: a TestRail migration reliably moves your test library and reliably loses or degrades your test evidence. Anyone telling you otherwise is either using API tooling with documented gaps or has not tried it on a large project.
Milestones, plans, configurations and users
These four are the quiet omissions, because most teams do not think about them until a report comes out wrong.
Milestones exist as full objects in the API, including sub-milestones, start and due dates and completion state. In a file export you get, at most, the milestone a case is attached to. The milestone structure itself, and anything hanging off it, has to be rebuilt or pulled through get_milestones.
Configurations, which is how TestRail models running the same suite against different browsers, devices or environments, appear in no file format. get_configs returns configuration groups with nested configurations, and note that no pagination is documented for it, unlike almost everything else.
Test plans are absent from CSV and XML, and TestRail's own line about runs and results applies to them too. aqua cloud and testomat.io both list test plans as not supported by their imports.
Users are the most restricted. get_users is admin-gated: any user can retrieve their own account, and retrieving anyone else's requires administrator access. From TestRail 6.6, non-administrators must pass a project ID, and when scoped to a project the response excludes inactive users. That last detail matters more than it sounds. The person who executed a critical run three years ago and has since left the company is exactly the record you want in an audit, and exactly the one a project-scoped call will not return. Plan the user extraction with an administrator account, once, before anything else, and keep the ID-to-name map. Every assignedto_id and created_by in every run and result is meaningless without it.
Case change history
TestRail keeps a per-case change log with old and new values, the field that changed, who changed it and when. It is reachable through exactly one door: get_history_for_case, available from TestRail 6.5.4. No file format documents it, and nothing in TestRail's export documentation acknowledges it exists.
For most teams this is acceptable collateral. For a team in a regulated environment where "who changed this expected result, and when" is a question an auditor asks, it is the single most expensive thing in this article, and it needs a decision at the start of the project rather than a discovery at the end. The realistic options are to crawl the history endpoint case by case and store the output as an archive, or to keep the TestRail instance readable for the retention period you are obliged to cover. Note that the second option has a cost that does not appear in any migration plan: a paid instance you keep alive purely so that history stays queryable.
The limits you will hit
The numbers below are documented and they shape how long an extraction takes.
| Limit | Value | Where it bites |
|---|---|---|
| API records per request | 250, and 250 is the documented maximum | Asking for more returns "Field :limit is too large (maximum 250)". Every large entity is a paged crawl using offset |
| Rate limit, TestRail Cloud Professional | 180 requests per instance per minute | Attachment downloads, at two calls per file, are what actually exhausts it |
| Rate limit, TestRail Cloud Enterprise | 300 requests per instance per minute | Same, with more headroom |
| Rate limit, TestRail Server | None built in | Self-hosted extractions can run flat out |
| 429 handling | Response carries a Retry-After header | Honour it; TestRail's documentation also puts the responsibility for retrying on 5xx errors on the client |
| Attachment size | Up to 256 MB per file on the TestRail side | The receiving system is usually the tighter constraint |
| CSV import size | 10 MB | An import-side cap in TestRail, not an export cap. Relevant if the destination copies TestRail's limits |
| Shared steps per response | 250 | Same paging discipline as everything else |
One number that gets quoted and should not be: there is no documented row cap or case-count cap on the UI export. The 10 MB figure is the import limit. If you read somewhere that CSV export tops out at some number of cases, that claim has no source in TestRail's documentation.
For a sense of realistic timing, testomat.io is the only vendor publishing per-volume figures for its own importer: 2–5 minutes for 100 cases, 10–20 minutes for 500, and 30–45 minutes for 1,000. A team at Rambler&Co documented a larger move on Habr, reporting that correcting the XML and running the automated export and import took roughly two hours for about 15,000 test scenarios, with media files handled by hand afterwards because their TestRail 5.5 instance predated the attachment API method introduced in 5.7. The pattern in both accounts is the same: the case library is fast, and the evidence around it is what consumes the week.
A field mapping you can start from
Most of the pain in a migration is not the transfer, it is deciding where each TestRail concept lands in the new system. This mapping is generic on purpose, so it works whichever destination you pick.
| TestRail concept | Typical destination | Watch out for |
|---|---|---|
| Project | Project | Teams often collapse several TestRail projects into one. Decide before import, not after |
| Suite | Top-level suite or folder | Single-suite and multi-suite TestRail projects export differently in practice |
| Section and sub-section | Nested suite or folder | Check the destination's nesting depth limit against your deepest branch |
| Case | Case | Case IDs will not be preserved unless the destination allows it. Keep the old ID in a custom field so old links and bug reports still resolve |
| Template (Text vs Steps) | Case template or layout | Mixed-template libraries need two passes; some importers only handle a subset of templates |
| Preconditions | Preconditions or a custom field | Xray promotes them to standalone entities, others treat them as text. Neither is wrong, both change the shape |
| Separated steps | Steps with expected results | The multi-row CSV layout is where silent regrouping happens |
| Shared steps | Shared or reusable steps | If the destination has no equivalent, decide between inlining and losing the link |
| Custom field | Custom field of matching type | Create fields with matching types before importing. Type mismatches are where values silently drop |
| References | Requirements or an issue link | Arrives as text. Re-linking is manual or scripted |
| Milestone | Milestone, release or version | Attachments and references on milestones commonly do not travel |
| Configuration | Environment or variable | API only. Rebuilding by hand is usually faster for a small set |
| Test run | Run or cycle | API only, and the model rarely matches one to one |
| Test and result | Execution record | API only. Expect to reshape into the destination's status model |
| User | User | Extract with an administrator account so inactive users are included |
Two things make this mapping easier to apply. Write it down before you touch either system, because the arguments about where suites should land are cheaper on paper than after an import. And keep the old TestRail case ID in a custom field on every case, since every bug report, commit message and Slack thread in your history refers to cases by that ID. The end-to-end process is in our guide to migrating test cases from spreadsheets, and the mechanics of the receiving side are in the knowledge base article on importing and exporting test cases via CSV.
What can be rebuilt on the other side, and what is gone
The useful distinction is not "did it export" but "can the destination reconstruct it". Three categories, and being clear-eyed about the third is what keeps a migration from turning into a disappointment.
Comes across cleanly. Suites, section hierarchy, cases, titles, steps and expected results, preconditions, priority and type, references as text, tags where the destination supports them. This is the bulk of the row count and the least of the work.
Reconstructable with effort. Custom fields, if you pull the definitions through get_case_fields and create matching types in the destination before importing. Attachments, if you script the two-call download and re-upload, and accept that inline images inside text need their markup rewritten. Milestones and configurations, which are usually faster to recreate by hand than to migrate. Users, from an administrator-scoped extraction. Old case IDs, if you deliberately keep them in a custom field.
Gone unless you archive it separately. Execution history in its original shape, with the runs, testers, timestamps, comments and evidence intact. Per-case change history. Result attachments tied to the run that produced them. You can archive all of it as JSON through the API and keep it as a static reference, and that is worth doing. What you cannot do is have it arrive in the new system as living, queryable history. Any team that needs it queryable is choosing between a scripted crawl into a purpose-built archive and paying TestRail to keep the lights on.
In QAM Hub specifically, the import path for a TestRail move is CSV, which means the library transfers and the evidence does not. We would rather say that plainly than imply an importer we do not ship. What we do differently is on the other side of the line: from the first run in QAM Hub, every execution keeps its full history per case across runs, with attachments and comments on results, so the gap you are inheriting is a one-time cost rather than a recurring one. If you are still choosing a destination, our comparison of TestRail alternatives covers nine tools with the migration path for each, and QAM Hub against TestRail is the head-to-head.
When a partial migration is the right call
Most of the time, and there is no shame in it. The decision framework we use with clients is short.
Migrate the library and archive the evidence when your regression suite is the asset and your execution history is mostly noise. This covers most product teams. Extract the case library, snapshot the last few months of results as JSON for reference, and start clean.
Migrate the library and a bounded window of history when recent trend data drives decisions, for example flaky-test tracking or release readiness reporting. Pick a window, three or six months, pull it through the API, and stop. BrowserStack's importer caps runs at one year and states it in the documentation, which is a reasonable model to copy even if you build the pipeline yourself.
Keep the old instance readable when you are in a regulated environment or under contractual audit obligations. Budget for it explicitly. It is a line item, not an oversight.
Reconsider the move entirely when the migration cost exceeds the annoyance you are trying to escape. If your library is 40,000 cases with heavy attachment use and deep shared-step reuse, and your actual complaint is reporting speed, get a quote for the reporting problem first.
One thing worth doing in every case: deduplicate before you import, not after. Migrating a bloated library carries the bloat into a system you are still learning, and the cleanup is harder there than in the tool you already know. Our guide to finding duplicate and low-quality test cases covers the audit, and the general case for structured test management over ad-hoc storage is in what a test management system actually is.
An export order that wastes the least time
- Turn on the API under Administration > Site Settings > API and generate a token. Do this even if you plan a file-based migration, because you will need the API for the definitions.
- Pull
get_case_fieldsfirst and save it. This is your decode table for every numeric ID you are about to meet. - Pull
get_userswith an administrator account, once, including inactive users. Store the ID-to-name map. - Export one section as XML and one as CSV, and open both. Check what your own instance does with dropdowns, references and shared steps before you trust any general claim, including the ones in this article.
- Export the library. XML if the destination accepts it, since it keeps the hierarchy and the step structure. CSV with separated steps on separate rows and the section hierarchy column included if it does not.
- Crawl attachments through
get_attachments_for_caseandget_attachment, at 250 records per page, honouring Retry-After. Keep the attachment ID in the file name so you can rewrite inline markup later. - Decide your history window, then crawl
get_runsandget_results_for_runfor that window, plusget_attachments_for_testfor result evidence. - Pull
get_milestones,get_configsandget_plansif you intend to rebuild them, or skip and recreate by hand. - Import into the destination, then validate on counts before you validate on content: cases per section, steps per case, non-empty custom fields per case. Count mismatches are the cheapest bugs to find.
- Keep the TestRail instance readable for a defined period after go-live. aqua cloud suggests two to four weeks, which is about right for a mid-sized team and too short if you are under audit.
Frequently asked questions
Does a TestRail export include attachments?
No. None of the three file formats contain attachment or image files. Testmo states that "TestRail does not include any attachments or images in the XML file", and Testiny says the same about its XML exports. Attachments are only retrievable through the API, using get_attachments_for_case for metadata and then get_attachment for each file. There is no bulk download.
Can you export test results and execution history from TestRail?
Not through the interface. TestRail has no UI export for runs or results, and its own migration documentation states that "test runs and results are tied to specific configurations and historical data that aren't supported through CSV/Excel or XML imports". The documented route is the API, calling get_runs for a project and then get_results_for_run for each run, which returns JSON.
Which TestRail export format loses the least data?
XML, for anything file-based. It keeps the nested section hierarchy and the structured step and expected-result pairs, and TestRail can re-import its own XML without manual mapping. It still contains no attachments, no images and no execution data, and it represents dropdowns, multi-selects, milestones and users as numeric IDs. For anything beyond the case library, the API loses the least, at the cost of writing code.
What is the difference between TestRail's CSV and XML export?
CSV and Excel let you choose sections and columns and offer a checkbox to put separated steps on separate rows, which turns one case into several rows. XML has no options, nests sections natively, and stores steps as structured elements. CSV needs manual column and value mapping on import; XML is the same format TestRail's own importer reads.
How many test cases can TestRail export at once?
TestRail documents no row or case-count cap on the UI export. The 10 MB limit that gets quoted is the CSV import cap. The real ceiling is on the API, where a single request returns at most 250 records and 250 is the documented maximum, so large extractions are paged crawls using the offset parameter.
Does TestRail have API rate limits?
TestRail Cloud does: 180 requests per instance per minute on Professional and 300 on Enterprise, with a 429 response carrying a Retry-After header when you exceed it. TestRail Server installations have no built-in rate limits. Attachment extraction is what usually hits the ceiling first, because each file costs two calls.
Will custom fields survive a TestRail migration?
The values will. The field definitions will not, in any file format. In XML, dropdown, multi-select, milestone and user values appear as numeric IDs with nothing to explain them, so pull get_case_fields before you export anything and keep the option lists it returns. Create matching field types in the destination before importing, since type mismatches are where values quietly disappear. Some tools do not attempt the mapping at all: aqua cloud's documentation says enabled custom field values are "added to the description of a test case" instead.
Can you get case change history out of TestRail?
Only through the get_history_for_case API endpoint, and only on TestRail 6.5.4 or later. No export format documents change history. If audit trails matter to your organisation, decide early between crawling that endpoint into an archive and keeping the TestRail instance readable for your retention period.
Is it worth exporting from TestRail at all, or should you start clean?
Export the library and be selective about the rest. The case library is the asset you spent years building and it transfers well. Execution history transfers badly everywhere, so decide deliberately how much of it you need queryable rather than archived. Teams that try to move everything are the ones whose migration slips, and the part that slips is always the evidence, not the cases.
How long does a TestRail migration take?
It depends on volume and how much media/history you carry over. Examples: roughly 2–5 minutes for 100 test cases and 30–45 minutes for 1,000 cases with a tool like testomat.io; Rambler&Co reported around 2 hours for 15,000 test scenarios with manual media handling.
References
- TestRail, "Export test cases", support.testrail.com, accessed August 2026.
- TestRail, "Import test cases from XML", support.testrail.com, accessed August 2026.
- TestRail, "Import test cases from CSV or Excel", support.testrail.com, accessed August 2026.
- TestRail, "Migrate from TestRail Server to TestRail Cloud", support.testrail.com, accessed August 2026 (source of the quotation on runs and results).
- TestRail, "Exporting test results", support.testrail.com, accessed August 2026.
- TestRail API reference: "Cases", "Case Fields", "Attachments", "Results", "Runs", "Milestones", "Configurations", "Users", "Shared Steps", support.testrail.com, accessed August 2026.
- TestRail, "Introduction to the TestRail API" and "Error handling", support.testrail.com, accessed August 2026 (pagination maximum, rate limits, Retry-After).
- TestRail, "Managing attachments", support.testrail.com, accessed August 2026 (256 MB limit, supported types).
- TestRail, "Data Storage and Management", support.testrail.com, accessed August 2026 (scheduled database export).
- Testmo, "Importing TestRail", support.testmo.com, accessed August 2026.
- Testiny, "Migrating from TestRail to Testiny", testiny.io documentation, accessed August 2026.
- Xray, "Importing TestRail test cases using Test Case Importer", getxraydocs.atlassian.net, accessed August 2026.
- Qase, "Import, export and migrating to Qase", docs.qase.io, accessed August 2026.
- aqua cloud, "Migration from TestRail", docs.aqua-cloud.io, accessed August 2026.
- testomat.io, "TestRail Migration Guide" and the migrate-testrail repository on GitHub, accessed August 2026 (per-volume duration figures, CSV limitations).
- QMetry, "Migrating from TestRail to QTM4J using Migration Tool", qmetrysupport.atlassian.net, accessed August 2026 (attachment size caps, exclusion list).
- BrowserStack Test Management, "Quick Import: TestRail", browserstack.com documentation, accessed August 2026 (one-year cap on runs and milestones, Cloud-only support).
- TestCollab, "Importing test cases from TestRail", help.testcollab.com, accessed August 2026.
- Zephyr, "Zephyr TestRail Project Migration", zephyrdocs.atlassian.net, accessed August 2026.
- Sam E. Lawrence, "Why we migrated from TestRail to Qase and how we did it", dev.to, accessed August 2026.
- Rambler&Co engineering team, TestRail to Test IT migration write-up, Habr, accessed August 2026 (15,000 scenarios, roughly two hours, manual media handling).
- TestRail community forum, "Bulk downloading attachments", discuss.gurock.com, accessed August 2026.
QAM Hub is a test management system built by QA Madness, who published this guide. TestRail behaviour above is drawn from TestRail's own current documentation, and competitor claims are quoted from each vendor's published documentation as of August 2026.