Skip to content

How to Export Data from Plone: Methods, API Limits & Portability

Complete guide to extracting data from Plone: REST API batching, collective.exportimport, plone.exportimport, .zexp, Data.fs — with version-specific recommendations and failure modes.

Rishabh Rishabh · · 18 min read
How to Export Data from Plone: Methods, API Limits & Portability
TALK TO AN ENGINEER

Planning a migration?

Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.

Schedule a free call
  • 1,500+ migrations completed
  • Zero downtime guaranteed
  • Transparent, fixed pricing
  • Project success responsibility
  • Post-migration support included

Plone has no single "Export Everything" button. Your export method depends on your Plone version, the content framework in use (Archetypes vs. Dexterity), and whether you're migrating to another Plone instance or leaving the ecosystem entirely.

Because Plone is built on Python and stores data in the Zope Object Database (ZODB) rather than a relational SQL database, you cannot run mysqldump or query business tables with SQL. Even when Plone uses PostgreSQL through RelStorage, the data is stored as pickled Python objects. The RelStorage FAQ confirms you cannot perform SQL queries against this data in the way most teams expect. To read it, you must wake up the Python objects within the Plone application context.

This architectural reality dictates every export method available to you. A migration-ready extract — content, blobs, users, permissions, relations, translations, redirects — typically requires combining more than one tool. A backup (copying Data.fs and blob storage) gives you a restorable copy of the same site, not an extract another platform can ingest.

Info

TL;DR: Use plone.exportimport for full-site JSON exports on Plone 6.1+. Use collective.exportimport for cross-version migrations from Plone 4–6. Use plone.restapi for programmatic, paginated extraction or continuous sync. Raw database backups are restore artifacts, not migration-ready exports. Each method has coverage gaps.

Plone Export Methods Compared

Method Content Users / Groups Relations Translations Version History Binary Files Best For
plone.restapi (@search) ✅ (paginated JSON) Via @users / @groups Separate calls Separate download Custom scripted extraction
plone.exportimport (@export) Opt-in (--include-revisions) ✅ (in ZIP) Plone 6.1+ site-to-site
collective.exportimport ✅ (base64, blob paths, or download URLs) Cross-version migration (Plone 4→6)
collective.jsonify ✅ (one JSON per object) Supplementary scripts ✅ (base64) Legacy Plone 2.1–4.x export
Zope .zexp (ZMI) Identical-version cloning only
Data.fs direct copy ✅ (entire DB) Blob storage separate Full backup / same-version restore
Transmogrifier pipelines Configurable Configurable Configurable Complex ETL pipelines

plone.restapi: The Primary API Export Path

plone.restapi is the standard programmatic interface for extracting content from Plone. It is included in Plone 6 when you install the Plone package. Version 10 requires Python 3 and works with Plone 6.2, 6.1, 6.0, and 5.2. On Plone 5.x, it is available as an installable add-on.

The API uses content negotiation — set the Accept header to application/json to get JSON responses. Authentication supports both HTTP Basic and JWT tokens.

Authentication

plone.restapi includes a Plone PAS plugin for JWT authentication, installed automatically with the product. To obtain a token:

curl -X POST http://your-plone-site/Plone/@login \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"login": "admin", "password": "secret"}'

The response includes a token field you use in subsequent requests via the Authorization: Bearer <token> header.

Batching and Pagination

Collection-like resources are batched if the result set exceeds the batch size. The default batch size is 25.

You control pagination with two query parameters:

  • b_start — Index of the first item in the batch (default: 0)
  • b_size — Number of items per batch (default: 25)

The response includes a batching object with hypermedia links (first, last, prev, next) that your client can follow. The items_total field gives you the total result count.

Unlike SaaS platforms with strict vendor-imposed rate limits, Plone is self-hosted. Your throughput limits are dictated by your server's RAM and WSGI server configuration (Waitress, Gunicorn). But you must still paginate — requesting too many heavy objects at once causes memory spikes and timeouts.

Warning

There is no official b_size=-1 parameter to disable batching. Setting b_size to a very large number works but can cause memory issues on large sites. The safest approach is to iterate through pages using the next link in the batching response.

The @search endpoint maps to Plone's portal_catalog. You can query by content type, path, date ranges, and any indexed field:

curl "http://your-plone-site/Plone/@search?portal_type=Document&b_size=100" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer <token>"

By default, @search returns catalog brain summaries — not full content. Adding ?fullobjects=true returns complete JSON representations, but Plone must wake each object from the ZODB, which is significantly slower. On a site with 100,000 items, fullobjects=true means 100,000 individual ZODB object loads; budget for this to take several hours on modest hardware.

Two details catch people regularly. First, @search on a container includes the container itself unless you constrain depth with path.depth. Second, SearchableText does not auto-append a trailing wildcard the way Plone's LiveSearch does — add the * yourself if you want prefix matching.

REST API Edge Cases

  • Summaries ≠ full objects. metadata_fields=_all returns less than fullobjects=true, and some fields requested as metadata can fail JSON conversion.
  • fullobjects=true has a real cost. For a site with 100,000 items, fetching each object individually means 100,000 ZODB object loads.
  • Folder GETs include children. Folderish content includes child items automatically; use include_items=false to avoid unexpected payload expansion.
  • Files are always a second hop. Serialized file/image fields give you filename, MIME type, size, and a download URL. You need a separate GET for the bytes.
  • User export supports CSV. The @users endpoint can return CSV as of plone.restapi 10.0.0rc1.
  • Groups are separate from users. The @groups endpoint defaults to 25 groups at a time unless you raise the limit parameter.
  • Registry export is admin-only. Reading or writing @registry records requires cmf.ManagePortal.

What the REST API Does Not Export

The REST API gives you content objects, users, groups, workflow states, and vocabularies. It does not provide a single-call export for:

  • Relations between objects (requires separate per-object inspection)
  • Content ordering (position in parent)
  • Local roles / sharing settings (per-object @sharing endpoint, not bulk-exportable)
  • Translation linkage (depends on plone.app.multilingual; no dedicated bulk export endpoint)
  • Portal configuration (registry, control panel settings)
  • Portlet assignments
  • URL redirects

For migration-grade extraction, the REST API is a building block — not a complete solution.

Step-by-Step: REST API Extraction Script

For teams writing custom extraction scripts, here's the practical workflow:

1. Authenticate and get a token:

TOKEN=$(curl -s -X POST http://localhost:8080/Plone/@login \
  -H "Content-Type: application/json" \
  -d '{"login":"admin","password":"secret"}' \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")

2. Enumerate content types:

curl -s "http://localhost:8080/Plone/@types" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

3. Paginate through all content:

import requests
 
base = "http://localhost:8080/Plone"
headers = {"Accept": "application/json", "Authorization": f"Bearer {TOKEN}"}
 
def export_all_content(portal_type):
    url = f"{base}/@search?portal_type={portal_type}&fullobjects=true&b_size=50"
    all_items = []
    while url:
        resp = requests.get(url, headers=headers).json()
        all_items.extend(resp.get("items", []))
        url = resp.get("batching", {}).get("next")
    return all_items

4. Download binary files separately:

for item in items:
    if "file" in item and item["file"]:
        file_url = item["file"]["download"]
        binary = requests.get(file_url, headers=headers).content
        # Save to disk

5. Export supplementary data:

curl -s "http://localhost:8080/Plone/@users" \
  -H "Accept: application/json" -H "Authorization: Bearer $TOKEN"
 
curl -s "http://localhost:8080/Plone/@groups" \
  -H "Accept: application/json" -H "Authorization: Bearer $TOKEN"

plone.exportimport: The Plone 6.1+ Official Export

plone.exportimport provides support to export and import all kinds of data from and to Plone sites using an intermediate JSON format. Most features use plone.restapi to serialize and deserialize data under the hood. This is the closest thing Plone has to a full site export — covering content, principals, relations, translations, discussions, redirects, and (optionally) version history. (Plone docs)

Two Export Interfaces

Command-line (server-side access required):

plone-exporter instance/etc/zope.conf Plone /tmp/plone_data/
plone-exporter --include-revisions instance/etc/zope.conf Plone /tmp/plone_data/

By default, revision history (older versions of each content item, stored in CMFEditions) is not exported. Add --include-revisions if you need it. On large sites with active editorial workflows, version history stored in CMFEditions can make your Data.fs 10–50× larger than the current content alone — exporting revisions substantially increases both export time and output size.

REST API endpoint:

curl -X POST \
  -H "Authorization: Bearer <token>" \
  https://plone.example.com/plone/++api++/@export \
  -o export.zip

The @export endpoint requires the plone.exportimport.export permission, granted to Site Administrators by default. The response is a ZIP file containing all exported data. On sites with 50,000+ objects, this endpoint runs synchronously and is prone to browser and HTTP proxy timeouts — use the command-line plone-exporter for large sites.

Export Structure

The exported layout is filesystem-friendly and easy to inspect. Each content item gets its own subdirectory named by UID, containing serialized data and blob files:

content/
  __metadata__.json
  plone_site_root/
  <UID>/
    data.json
    file/<filename>
principals.json
relations.json
translations.json
discussions.json
redirects.json

This structure makes it straightforward to diff, inspect, and transform compared to a raw database snapshot.

Warning

plone.exportimport targets current Plone and Python versions only. It does not support Plone 4/5 or handle Archetypes-to-Dexterity conversion. If you're on an older version, you need collective.exportimport instead.

collective.exportimport: The Cross-Version Migration Workhorse

This is the package the Plone community uses for major version migrations. It allows you to migrate from Plone 4 to 6, from Python 2 to 3, and from Archetypes to Dexterity in one step. Plone's own upgrade documentation recommends it for large and complex migrations.

What It Exports

Each data type has its own browser view:

  • /@@export_content — Content items (select by type or export all)
  • /@@export_members — Users, groups, and role assignments
  • /@@export_relations — Object relations
  • /@@export_localroles — Per-object sharing/permissions
  • /@@export_translations — Translation links
  • /@@export_ordering — Item positions within folders
  • /@@export_discussion — Comments/discussions

If you select "Save to file on server," the export saves JSON files to the <var> directory of your Plone instance. The import view looks for files under /var/instance/import.

Blob Handling Strategies

collective.exportimport gives you three strategies for binary files:

  1. Base64 encoding: Embeds the file directly in the JSON. Avoid for large sites — base64 inflates binary size by approximately 33% and loading all blobs into memory simultaneously causes severe memory bloat.
  2. Blob paths: Exports the absolute filesystem path to the blob. Plone's migration training recommends this option when you can access the old blob store directly. This is the most memory-efficient option.
  3. Download URLs: References the file via URL for later retrieval. Useful when the source site remains accessible during import.

Key Constraints

  • Export format is JSON — one file per content type (e.g., Document.json, Image.json)
  • No version history — only current versions are exported
  • Modified dates get reset during import. A separate @@reset_dates view resets modification dates to the values in the exported JSON, since import-time operations update the modified date. The original is stored as obj.modification_date_migrated on each new object.
  • Requires server-side access — you must install the package and access the Plone instance directly
  • Performance is significant. Per the project README, importing 5,000 Documents takes approximately 25 minutes with versioning enabled and approximately 7 minutes without. For a 50,000-document site, that translates to roughly 4–5 hours of import time with versioning. Rehearse with production-like data volumes before scheduling a maintenance window.

The Volto Block Problem

If you're exporting from Plone 6 with Volto (the modern React frontend), you'll hit a data mapping challenge that no export tool solves on its own.

In Classic Plone, rich text is stored as standard HTML in a RichTextValue field. Most target systems — WordPress, Contentful, SharePoint — can ingest HTML directly.

In Volto, content is not stored as HTML. It lives in the blocks and blocks_layout fields as Slate JSON. A simple bold paragraph looks like this in the export:

{
  "@type": "slate",
  "plaintext": "This is a paragraph.",
  "value": [
    {
      "children": [
        { "text": "This is a " },
        { "text": "paragraph.", "strong": true }
      ],
      "type": "p"
    }
  ]
}

No other CMS natively understands Plone's Volto block schema. Much like dealing with proprietary HubL code during a HubSpot CMS export, you will need middleware to parse the blocks JSON and convert it into either standard HTML or the specific block format of your target system. The mapping is non-trivial:

Volto Block Type WordPress Equivalent Contentful Equivalent Sanity Equivalent
slate (rich text) core/paragraph block Rich Text field Portable Text array
image core/image block Asset reference Image object
video core/embed block No native equivalent External URL field
listing Dynamic query block (plugin) No native equivalent Custom query type
toc (table of contents) generateblocks/table-of-contents No native equivalent Custom component
Custom add-on blocks Requires custom plugin Requires custom field type Requires custom schema

The blocks_layout field stores the display order as an array of UUIDs referencing keys in the blocks object. Any conversion tool must respect this ordering and handle blocks it doesn't recognize gracefully (typically by falling back to the plaintext property if present, or skipping the block).

Tip

When migrating to a non-Plone target, exporting via plone.restapi with fullobjects=true gives you the most portable JSON serialization, including resolved field values rather than internal references. For Volto sites, this is the only way to get the full blocks payload — the catalog brain summaries returned by @search without fullobjects=true omit block content entirely.

collective.jsonify: Legacy Plone Export (2.1+)

For very old Plone installations where neither plone.restapi nor collective.exportimport can be installed, collective.jsonify is the fallback. Its only dependency is simplejson, and it can be installed on any Plone version as far back as Plone 2.1.

The export produces one JSON file per content object, executed via External Methods registered through the Zope Management Interface (ZMI). The output format is collective.transmogrifier-compatible, meaning each JSON file maps directly to a pipeline item that transmogrifier can process — it is not directly importable by WordPress, Drupal, or other non-Plone platforms without an additional transformation layer.

To migrate Plone 1, 2, or 3 to Plone 6, the recommended pipeline is collective.jsonify for export and collective.exportimport for import.

Limitations:

  • No export of relations, translations, or local roles without supplementary scripts
  • No web UI or progress indicator — export progress must be monitored via Zope logs
  • Base64-encoded binaries (memory-intensive for large file collections)
  • No version history export
  • Output is collective.transmogrifier-compatible JSON, not directly importable by non-Plone systems

Transmogrifier Pipelines: ETL for Plone

Transmogrifier is a pipeline-based ETL tool for Plone content migration. Pipelines are defined in .ini-like configuration files. Each pipeline consists of ordered blueprints — reusable processing components that each receive a content item dict, optionally transform it, and pass it to the next blueprint in the chain.

A minimal transmogrifier pipeline for exporting content to JSON looks like:

[transmogrifier]
pipeline =
    source
    exporter
    saver
 
[source]
blueprint = collective.transmogrifier.sections.csvsource
filename = items.csv
 
[exporter]
blueprint = collective.transmogrifier.sections.logger
name = exporter
 
[saver]
blueprint = collective.transmogrifier.sections.jsonwriter
directory = /tmp/export

Blueprints can read from the ZODB directly, apply field transformations, filter by content type, rename attributes, or write to arbitrary output formats. This flexibility makes Transmogrifier suited for migrations that require content transformation during export — renaming fields, splitting content types, merging folders. The trade-off is a steep learning curve and the requirement for deep Plone engineering knowledge. For straightforward exports, collective.exportimport has largely replaced it.

Exporting Users, Groups, and Permissions

Plone handles authentication and authorization via the Pluggable Auth Service (PAS). Exporting content does not automatically export users or their permissions — these are separate data tracks.

  • With collective.exportimport, run @@export_members for users and groups, and @@export_localroles for per-object permissions.
  • With plone.restapi, users come from @users, groups from @groups, and local roles from the per-object @sharing endpoint (not bulk-exportable in a single call).
  • With plone.exportimport, users and groups are included in the principals.json output automatically.

Local roles deserve particular attention. Plone allows permissions at the folder or document level — granting a specific user "Editor" rights on a single subfolder, for example. The permission model uses the following built-in roles: Reader, Editor, Contributor, Reviewer, Site Administrator. Extracting the granular per-object permission matrix requires walking the full object tree and calling @sharing on each object, or using @@export_localroles from collective.exportimport.

If you're migrating to a system with a simpler permission model (Webflow, basic SharePoint sites, most headless CMS platforms), you'll need to flatten this architecture into something the target can represent — typically by mapping all local role assignments to a single flat permission tier.

Exporting to WordPress: Field Mapping Reference

"Plone to WordPress" is one of the most common migration queries. WordPress ingests content via the WordPress REST API or the WXR (WordPress eXtended RSS) XML format. Here is how Plone fields map:

Plone Field WordPress Equivalent Notes
title post_title Direct mapping
description post_excerpt Direct mapping
text (Classic HTML) post_content Direct mapping; HTML passthrough
blocks (Volto JSON) post_content as Gutenberg blocks Requires conversion; no standard tool exists
effective (publication date) post_date ISO 8601 format; convert timezone
subjects (tags) tags Create terms first, then assign by ID
creators post_author Must pre-create WP users; map by username
review_state post_status Map publishedpublish, privatedraft
image (lead image) _thumbnail_id (featured image) Upload via media endpoint, then set meta
UID Custom meta field (_plone_uid) Store for redirect resolution
Original path Custom meta field (_plone_path) Required for redirect rule generation

There is no off-the-shelf Plone-to-WordPress migration tool. The standard approach is: export via plone.restapi or collective.exportimport, write a Python or Node.js script to transform the JSON, and POST to the WordPress REST API or generate WXR XML for import.

Choosing the Right Export Method by Plone Version

Plone Version Best Export Tool Notes
2.1–3.x collective.jsonify Only option; pair with collective.exportimport for import on target
4.x (Archetypes) collective.exportimport Handles AT→Dexterity conversion; install via buildout
5.0–5.2 collective.exportimport + plone.restapi REST API available as add-on on 5.x, core on 5.2
6.0 collective.exportimport or plone.exportimport Volto frontend; REST API is core
6.1+ plone.exportimport Ships with Plone; @export endpoint available

Decision by Scenario

Scenario Recommended Method Why
Plone 6.1 → Plone 6.1 plone.exportimport Official, complete, single ZIP
Plone 4 → Plone 6 collective.exportimport Handles AT→Dexterity, Python 2→3
Plone 2.x/3.x → anything collective.jsonify + collective.exportimport Only viable path for ancient versions
Plone → WordPress plone.restapi scripted export + custom transform Most portable JSON; requires field mapping script
Plone → Contentful/Sanity plone.restapi scripted export + custom transform Export schema must map to Contentful/Sanity content model
Plone → Drupal collective.exportimport JSON + Drupal Migrate API Drupal's migrate framework can consume structured JSON
Full backup / disaster recovery Data.fs + blob storage copy Complete, but version-locked
Complex content transformation Transmogrifier pipeline Maximum flexibility, highest effort

ZEXP and Data.fs: Backup Methods, Not Export Methods

ZEXP

Zope's native .zexp format is available through the Zope Management Interface (ZMI). Navigate to the object, click "Import/Export," and download the file.

Danger

ZEXP files are Python pickle data. Both source and target Zope instances must be identical — same Zope version, same Products installed, same Python version. Even minor version differences can cause unpickling failures that are difficult to diagnose. Do not use .zexp for cross-version migrations or export to non-Plone systems.

Data.fs Direct Copy

Every Plone site stores its content in the ZODB, typically as Data.fs in var/filestorage/, with blob storage in var/blobstorage/. These are maintained synchronously; if they drift out of sync, you encounter POSKeyError errors when content is read.

This is a backup method, not an export method. The files are only usable by an identical or closely compatible Plone installation.

Data.fs copy is useful for: moving between identical servers, performing in-place upgrades within the same major version line, or creating a staging copy to run collective.exportimport against before the actual migration window.

Warning

Do not hand a Data.fs or RelStorage snapshot to the target-system team and call it the export. Use backup tooling when the question is "How do we restore this Plone site?" Use export tooling when the question is "How do we transform this data for another system?" Those are different jobs, and mixing them up creates bad migration plans.

Common Failure Modes

Here are the failure patterns that appear most frequently in Plone export and migration projects:

  1. Blob storage not included in backup. Data.fs doesn't contain large files if blob storage is enabled (standard since Plone 4). You must copy var/blobstorage/ separately and keep the two in sync. A Data.fs without its matching blob store produces POSKeyError on any content with file attachments.

  2. Archetypes content on Plone 6. If you skipped the AT→Dexterity migration, your content types may not serialize correctly via plone.restapi. Run the AT→Dexterity migration step first using plone.app.contenttypes, or use collective.exportimport which handles the conversion inline.

  3. Large sites timing out. The @export endpoint and @@export_content views run synchronously. On sites with 50,000+ objects, browser and reverse-proxy timeouts are common. Use the command-line plone-exporter or the "Save to file on server" option in collective.exportimport.

  4. Revision history bloat. Plone stores content versions in CMFEditions. These are not exported by default and can make Data.fs files 10–50× larger than the actual current content. If you export with --include-revisions, budget for substantially longer export and import times.

  5. Missing add-on content types. If your site uses add-on types (PloneFormGen, eea.facetednavigation, collective.dexteritytextindexer-dependent types), the export tools may not serialize their custom fields correctly without explicit serializer adapters registered for those types.

  6. Encoding issues on Python 2. Plone 4 sites running Python 2.7 can produce JSON with mixed byte strings and unicode. collective.exportimport handles this internally, but custom scripts that concatenate or process the raw output may break with UnicodeDecodeError.

  7. Form data schema drift. KU Leuven's Plone 6 migration found that saved form data failed when stored rows no longer matched the current form structure. They kept the old Plone 4 site available for at least one year so users could view and download legacy data. (KU Leuven migration notes)

  8. Modified dates silently change. Import-time operations update modified dates. Run @@reset_dates after import if date integrity matters for audit trails or publication history.

  9. Volto blocks with no plaintext fallback. Some custom Volto block types store no plaintext representation. If your conversion script relies on plaintext as a fallback for unrecognized block types, those blocks will silently produce empty content in the target. Audit custom block types before export.

  10. portal_catalog out of sync. On old or poorly maintained Plone sites, the catalog may not reflect actual content (objects present in ZODB but not indexed, or catalog entries pointing to deleted objects). Run a full catalog rebuild before export to avoid gaps between @search results and actual content.

Pre-Migration Export Checklist

Because Plone splits content, blobs, roles, redirects, and configuration across different tracks, your runbook should do the same.

  1. Inventory by object class, not just page count. Count custom types, files/images, forms, multilingual branches, comments, and redirects. Query portal_catalog by portal_type to get per-type counts.
  2. Choose the export path by Plone version. See the version table above.
  3. Decide blob strategy early. REST download, per-item files on disk, base64 in JSON, or legacy blob-path access all have different performance and validation costs. Base64 is generally unsuitable for sites with more than a few hundred MB of binary content.
  4. Export secondary objects deliberately. Do not assume users, groups, local roles, aliases, translations, relations, or version history come along "for free." Each requires an explicit export step.
  5. Preserve stable identifiers. Keep UID, original path, and published URL history wherever the target can store them. These are essential for generating redirect rules.
  6. Rebuild the catalog before export. Ensures @search results match actual content. Run through ZMI → portal_catalog → "Advanced" → "Clear and Rebuild."
  7. Validate with counts and spot checks. Compare object counts by type, file counts, revision expectations, and redirect behavior between source and target.
  8. Test Volto block conversion separately. If the source is a Volto site, extract a representative sample of each custom block type and verify your conversion produces correct output before running full export.
  9. Rehearse with production-like data. Run at least one full dry run to measure timing, fix edge cases, and document rollback steps. Keep the old site available long enough for delta checks.

Getting the Data Out Is the Easy Part

Plone migrations are among the more technically demanding data extraction jobs. The combination of ZODB storage, version-dependent serialization, the Archetypes-to-Dexterity transition, and Volto's block format means there is no "one-click" path — especially when the target isn't another Plone instance.

The hard part is rarely getting bytes out of Plone. As is true when extracting data from other complex enterprise platforms, the hard part is rebuilding structure in the target: binaries, roles, redirects, translations, and content relationships — without losing trust in the data. The Volto block schema, the local roles matrix, and add-on content type fields are the three areas most likely to require custom engineering regardless of which export tool you choose.

Frequently Asked Questions

Can I export Plone data using a SQL database dump?
No. Even if Plone uses PostgreSQL via RelStorage, the data is stored as serialized Python pickles. The RelStorage FAQ confirms you cannot perform useful SQL queries against this data. You must extract data through Plone's Python environment using tools like collective.exportimport, plone.exportimport, or plone.restapi.
How do I export all content from a Plone site?
On Plone 6.1+, use the plone.exportimport @export endpoint or plone-exporter CLI to get a ZIP of all content, users, relations, and translations. On Plone 4–5, install collective.exportimport and use the @@export_content browser view. For Plone 2.x–3.x, use collective.jsonify (one JSON file per object) and pair it with collective.exportimport for import on the target.
Does the Plone REST API have rate limits?
Plone is self-hosted, so there are no vendor-imposed rate limits. However, plone.restapi paginates results with a default batch size of 25 items (b_size parameter). Performance is bound by ZODB I/O — using fullobjects=true on large result sets can be very slow as each object must be woken from the database.
What is the difference between collective.exportimport and plone.exportimport?
collective.exportimport supports Plone 4–6, handles Archetypes-to-Dexterity conversion, and works with Python 2 and 3. plone.exportimport ships with Plone 6.1+, focuses only on latest Plone/Python, and provides a REST API @export endpoint. Use collective.exportimport for cross-version migrations and plone.exportimport for Plone 6.1+ exports.
What format does Plone 6 Volto export rich text in?
If you are using Plone 6 with the Volto frontend, rich text is stored as Slate JSON blocks in the blocks and blocks_layout fields, not standard HTML. No other CMS natively reads this format — you will need to write middleware to convert the block JSON into HTML or your target system's block schema.

More from our Blog