# How do I export WordPress custom post types to JSON?

> Export custom post types from WordPress to JSON through the REST API, which returns each post type's items as structured JSON when the type is registered with REST support. If a type is not exposed, you enable REST for it or pull it from the database, and if you have no admin access at all, you crawl the front end and convert the rendered pages to JSON. The XML (WXR) export is the wrong tool here because it often skips custom post types and flattens custom fields. WPBuildAI scans the live site and returns custom post types and their fields as clean JSON, even without admin.

Source: https://wpbuildai.com/how-export-custom-post-types-wordpress-json/
By lawrence-arya · 2026-06-04

---
You export custom post types from WordPress to JSON through the REST API, which returns each registered post type's items as clean, structured JSON. That is the right tool, and it matters because the obvious tool, WordPress's built-in XML export, is the wrong one: the WXR file often skips custom post types entirely and flattens custom fields, so a migration that relies on it can silently leave a whole content type behind. When a type is exposed to REST you pull it directly; when it is not, you enable REST for it or read the database; and when you have no admin access at all, you crawl the front end and convert the rendered pages. WPBuildAI scans the live site and returns custom post types and their fields as JSON, even without admin.

Custom post types usually belong to a plugin rather than to core, which is where the fragility sits: Patchstack attributed [91 percent of the 11,334 WordPress vulnerabilities disclosed in 2025 to plugins and only six to core](https://patchstack.com/whitepaper/state-of-wordpress-security-in-2026/), so the code defining your content shape is also the code most likely to be abandoned.

## Why the XML export misses custom post types

The XML export was built for moving a site between WordPress installs, not for getting structured data out. Custom post types are registered by a theme or plugin, and the WXR export only includes a type if it is explicitly told to, which many are not, so portfolios, products, testimonials, and other custom types frequently do not appear in the file at all. Even when they do, their custom fields are serialized into a form another system cannot read cleanly. The 2024 [Web Almanac](https://almanac.httparchive.org/en/2024/) reflects how much WordPress content lives in plugin-defined structures, which is exactly what the XML export handles worst. The result is an export that looks complete and is missing your most specific content, the same failure described in [why a WordPress XML export fails](/why-wordpress-xml-export-fails/).

## Route 1: the REST API when REST support is on

The cleanest route is the [WordPress REST API](https://developer.wordpress.org/rest-api/). A custom post type registered with REST support exposes its own endpoint that returns its items as structured JSON, with each field separated, so you get clean data rather than parsed HTML. You can read it with any HTTP client, no scripting required for a one-off pull. Each item arrives with its title, content, slug, date, and registered fields, which maps directly onto a new platform's import. When the type was registered properly, this is a matter of reading the endpoint and saving the JSON, which is why it is the first thing to try before anything more involved.

## Route 2: enable REST for a type you control

If a custom post type is not exposed to REST and you have admin access, you can enable it. A post type registered in your own theme or a plugin you control can be given REST support, after which it behaves like any other endpoint and returns JSON. Custom fields can be added to the REST response so they travel with the item rather than being left behind. This route suits sites where you own the code and the type was simply registered without REST in mind. Once enabled, the export falls back to Route 1, and the data comes out clean.

## Route 3: crawl the front end when you have no admin

When you are locked out of the admin, common in a rescue, a dispute, or a lost-password situation, you can still recover the content by crawling the public pages. The published front end shows the custom post type's content as rendered HTML, and a crawler reads it the way a search engine does and converts it to JSON. This is the core of [extracting data from a hacked or locked WordPress site](/extract-data-from-hacked-wordpress-site/): you do not need database or admin access to capture what is publicly visible. It is also the only route when the site is compromised and you want the content without touching the infected backend. The trade-off is that you get what is rendered, not private fields, but for most content that is enough.

## Custom fields (ACF) need explicit handling

Custom fields are where exports quietly lose data, so they deserve their own attention. Fields added by Advanced Custom Fields or similar are stored as post meta, and they only appear in a REST response if they are registered to show there, otherwise they are invisible to the export even when the post itself comes across. So part of exporting a custom post type is confirming its fields are included, by exposing the meta to REST, or by reading it from the database, or by capturing it from the rendered page where it is displayed. A portfolio item without its project fields, or a product without its specs, is a half-migrated record, which is worse than an obvious gap because it looks done.

## What to capture for each item

A complete export of a custom post type captures, for every item: the title, the body content, the slug and full URL, the publish date, the taxonomy terms (categories, tags, or custom taxonomies), the featured and inline images with alt text, and every custom field. Missing any of these thins the record. The URL matters as much as the content, because it is what the redirect map is built from, and traffic is concentrated: the Ahrefs [search traffic study](https://ahrefs.com/blog/search-traffic-study/) found a small share of pages earns most visits, so a custom post type that happens to rank well must come across complete, URLs included.

## Keeping URLs and rankings when CPTs move

Custom post types usually have their own URL structure, and moving them is a migration like any other. Keep the same paths in the new system where you can, so the items do not move, and set a single-hop 301 for any URL that changes, following Google's [site move guidance](https://developers.google.com/search/docs/crawling-indexing/site-move-with-url-changes). This is the step most likely to be skipped for custom post types, precisely because they were nearly forgotten in the export, so their URLs end up with no redirect and 404 after launch. Treat the custom type's URLs with the same care as the main pages, and they keep their rankings through the move.

## A worked example: a portfolio CPT

A studio site had a `project` custom post type with 80 portfolio items, each with ACF fields for client, year, and gallery. The XML export contained the blog posts and pages but not a single project, which the owner only noticed because the count looked wrong. Switching to the REST API, the `project` endpoint was available but the ACF fields were not in the response, so they were exposed to REST and the 80 items then exported as JSON with their client, year, and gallery fields intact. Images came across with alt text. The project URLs were kept identical in the new build, so no redirects were even needed for them. The content type that the XML export had silently dropped migrated complete, because the export was checked against the real count.

## Common mistakes exporting custom post types

- Trusting the XML export and not noticing a whole post type is missing from it.
- Exporting the posts but not their custom fields, leaving half-complete records.
- Forgetting the custom type's URLs in the redirect map, so they 404 after launch.
- Assuming you need admin or database access when a front-end crawl would recover the content.
- Not checking the exported item count against the live count before migrating.

## Key points to remember

Export WordPress custom post types to JSON through the REST API, which returns clean structured data when the type has REST support; enable REST or read the database when it does not, and crawl the front end when you have no admin. Avoid the XML export for this, because it often skips custom post types and flattens their fields. Capture every field, image, and URL per item, confirm the count against the live site, and 301 the custom type's URLs like any other so rankings survive. The data that is easiest to forget is the data the XML export hides. WPBuildAI scans the live site and returns custom post types and their fields as JSON, even without admin. Send your site URL for a free migration assessment.

Not affiliated with WordPress, Lovable, Webflow, Shopify, Wix, or Squarespace.