skip to content →
dispatch_log DISPATCH_005FILED MAR 3 2026OPERATORSOPHIE LAURENTLIVE
DISPATCH_005PRODUCTMar 3, 202610 min read

8 CMS integrations and what we learned building each one

WordPress, Wix, Shopify, Squarespace, Blogger, Zapier, Custom API, Self-Hosted. A behind-the-scenes tour of every CMS adapter we've built, the quirks we hit, and why we still haven't shipped some of the ones you'd expect.

A year ago, VectraSEO had exactly one way to publish a blog post: you copied the generated HTML into your clipboard and pasted it into your CMS. It worked. It was also deeply unsatisfying, because the promise of the product is automation, and manual copy-paste is the opposite of automation.

We now have eight CMS adapters. Some were straightforward. Some took weeks of work for edge cases we didn't anticipate. A few CMSs that seem like obvious integrations we still haven't built, for reasons I'll explain. This is the tour.

WordPress: the reference implementation

WordPress was our first. It powers somewhere around 40% of the web and a disproportionately larger share of blog-focused sites. If we couldn't publish to WordPress, we didn't have a product.

The good news: WordPress has a well-documented REST API. Authenticate, POST to /wp-json/wp/v2/posts, done. The bad news is that the "authenticate" step is where WordPress has 1000 variations. Basic auth. JWT plugins. OAuth 2. Application Passwords (introduced in WP 5.6, which we settled on as our default because it's the only native option that's still maintained).

What surprised me: the feature variation across WordPress installs is massive. Some have Gutenberg. Some have the Classic Editor plugin. Some have a custom block theme. Our adapter produces HTML that works across all of these — we ship the post body as a single HTML block, which Gutenberg handles fine and the classic editor treats as a standard post body. We learned this the hard way after shipping Gutenberg-native blocks first and breaking publish on half our early installs.

We also write featured image + meta description + categories + tags + excerpt. Featured images required setting up a media upload flow; you have to POST the image to /media first, get the media ID, then reference it in the post create. Three API calls per publish. Fine, but more ceremony than I expected.

Wix: the curveball

Wix was the second integration we built and the one that taught me to never assume "has an API" means "has an API that can do what you need."

Wix has an API. The API lets you do many things. It does not, at the time we built our adapter, let you programmatically publish a new blog post to a Wix site's blog. You can do everything else — fetch existing posts, update existing posts, manage users, manage collections. But creating a new blog post via API? Not exposed.

What we ended up building: an adapter that creates a Wix Velo (their in-site code execution platform) webhook. The Wix site owner installs a small script we generate. When we want to publish, we POST to the webhook, the webhook runs server-side inside Wix with the permissions to create a post, and the post appears. It's not elegant. It's how Wix Velo is designed. It does work.

The installation flow is rougher for Wix than for any other integration — the site owner has to paste code into their Wix dashboard. We try to soften it with a click-to-copy install page and detailed screenshots. Wix customers who get through the setup are happy. Some bounce during setup. This is honest trade-off territory.

Shopify: blog vs product

Shopify is nominally an e-commerce platform, but a surprising number of Shopify stores have active content strategies — they publish gift guides, style articles, how-tos, and other long-form content to drive organic traffic. So Shopify made our list early.

The quirk with Shopify is that the Blog API is separate from the Product API and lives under a different permission scope. We had to register a separate OAuth scope (write_content), and we had to decide — in the adapter configuration — which of the store's blogs (yes, Shopify supports multiple blogs per store) to publish to. For stores with one blog, this is invisible. For stores with multiple, we show a dropdown.

We do not touch the Product API. VectraSEO doesn't generate product descriptions, and conflating blogs and products would make the UX muddy. One responsibility per integration.

Interesting side note: Shopify's blog API is substantially older and less polished than the rest of their platform. It's the kind of endpoint where the documentation warns about rate limits but doesn't document the exact limit. We cap ourselves at 2 requests per second and have never hit a rate limit.

Squarespace: the wall

Squarespace has an API. The API does not let you create blog posts. This is not a typo. You can fetch. You can list. You cannot POST.

So we had to get creative. Squarespace supports IFTTT / Zapier-style triggers, where an external event can trigger a blog post creation. Our adapter uses a private unpublished Squarespace extension pattern — the site owner configures a "publishing inbox" address (an email that Squarespace monitors), and we email formatted posts to that address. Squarespace receives the email and creates a draft post.

It's a draft, not a published post. Someone on the customer's team has to go hit "Publish" on each post. This is a workflow compromise. Squarespace customers tell us they're OK with it — having the draft sitting there waiting is still dramatically faster than writing from scratch — but I want to be honest that it's a two-click publishing flow, not a zero-click one.

If Squarespace ever ships a proper blog creation API, this adapter gets rewritten in a day. Until then, email-to-draft is the least-bad option.

Blogger: the antique

Blogger is old. Blogger is a Google product that Google has not meaningfully updated in roughly a decade. But Blogger still powers a surprising tail of sites, especially in specific niches (craft blogs, specific non-English markets, long-running personal blogs).

The good news about Blogger: it's a Google product, so the API uses standard Google OAuth, which we already had infrastructure for. The bad news: Blogger's API docs were last meaningfully updated in 2016. Some of the fields described don't exist anymore. Some of the fields that exist aren't documented. We had to reverse-engineer about 20% of what we built.

Blogger has a relatively small share of our customer base, but the customers who use it are loyal to it. Adding this adapter took about three days of engineering. Worth it for the goodwill.

Zapier: the escape hatch

Zapier isn't a CMS. But it's how we cover the long tail of CMSs we haven't built native adapters for.

Our Zapier integration exposes a single trigger: "new post ready to publish." When VectraSEO generates a post, we fire the trigger with the post payload. The customer configures whatever Zapier does next — typically a POST to their CMS, or an email, or a Slack notification.

The reason this works is that most custom or long-tail CMSs are integrated into Zapier by someone else. If you're on Ghost or Webflow or Craft CMS or Notion, chances are there's a Zapier action that accepts a blog post payload. We are the source. Zapier is the pipe. Whoever built your CMS's Zapier integration is the destination.

Building a Zapier adapter cost us about a week of engineering and unlocked probably 30+ indirect CMS integrations. Best leverage ratio of anything we've shipped.

Custom API: the power user tool

Some of our enterprise customers have home-grown CMSs, or CMSs with bespoke API requirements that don't fit Zapier. For those, we built a Custom API adapter.

The configuration surface: endpoint URL, HTTP method, authentication headers (we support Bearer, Basic, API key in header, API key in query), and a payload template with variable substitution (${post.title}, ${post.html}, etc.).

This is the "eject" button. If your CMS has any kind of REST API that accepts a POST, you can almost certainly make the Custom API adapter work with enough configuration.

The support burden for this one is higher than for the native adapters. Customers sometimes configure it incorrectly, and our support team has to debug JSON payload formatting against their CMS's requirements. We've moved toward shipping configuration templates for common CMSs (Ghost, Strapi, Contentful) to reduce that load, but it's still the adapter that generates the most tickets.

Self-hosted: the opinionated one

The Self-Hosted adapter isn't really a CMS integration — it's our opinionated solution for customers who don't have a CMS and want the simplest possible publish target.

It works like this: when you select Self-Hosted as your publish destination, we write the post directly to our vectraseo-blog S3 bucket, and it becomes live at yourproject.vectraseo.com/blog/post-slug. Rendered as clean HTML, mobile-friendly, with the meta tags we generated. Served from CloudFront.

This is for customers who are starting from zero and don't want to pick a CMS before they pick a content strategy. Publish a few pieces on the Self-Hosted subdomain. If you later set up a proper CMS on a custom domain, we can help migrate.

It's been more popular than I expected. About 15% of new signups pick Self-Hosted. The common user pattern is: "I want to test whether content marketing works for my business before I commit to a CMS."

What we haven't built (yet)

Some conspicuous absences:

Webflow. On the roadmap. Webflow has an API, the API works, the reason we haven't shipped is that Webflow sites often use CMS Collections with custom fields per site, and our adapter needs to know how to map our generic post schema to whatever fields that specific site has. We're designing a config-driven mapping layer and it's taking longer than we wanted.

Ghost. Supported via Custom API with a template. No native adapter yet because Ghost's admin API requires session cookies, not tokens, which complicates our auth model. We'll get there.

Contentful / Strapi / Sanity. Headless CMSs. Supported via Custom API. The reason there's no native adapter is that each one has a schema that varies per project, and there's no generic "publish a blog post" flow we can bake in.

HubSpot. Has an API. We've scoped the work. It's next up after Webflow.

The meta-lesson

Building CMS adapters has taught me something I didn't expect: the integration isn't actually about the CMS. It's about the workflow around the CMS. Which permissions are approved. Who hits publish. Which fields are mandatory. How drafts move to review to live.

The CMSs that are easy to integrate aren't necessarily the best-engineered ones. They're the ones whose workflow maps cleanly to our assumption: "accept a complete blog post payload and publish it." WordPress does this well. Wix and Squarespace fight it. Shopify is in between.

If you're building CMS adapters for your own product, start with whatever your customers use most. Build that one deeply, including the edge cases. Then build Zapier. Then Custom API. Native integrations for the next three most-requested CMSs. That order has served us well.

What's next? You tell me. The integrations page has a "request an integration" form. We build the ones customers actually ask for.

[ END_OF_DISPATCH ]
SL
Sophie Laurent
Head of Product — VectraSEO

Field reports filed by operators who actually run the system. If something in this dispatch is wrong, tell us — dispatch@vectraseo.com.