More agencies now edit Elementor pages without opening the Elementor editor. They write to _elementor_data through the REST API, WP-CLI, a custom bridge plugin or an AI agent. It's fast, and it lets you update 30 pages in the time it takes to open one.
It also brings a class of bugs that never show an error. The write succeeds, the API returns 200, and the page looks wrong or doesn't change at all.
We hit three of these in one month across client sites. Each one cost time because the obvious explanation ("my write failed") was wrong.
Cause 1: The host is serving an old copy of the page
The symptom: you update a page, fetch it, and get the old version back, byte for byte.
On one Hostinger-hosted WordPress site, our writes to _elementor_data were landing correctly in the database. The live page kept serving the previous HTML anyway. What made it hard to spot:
- There was no cache header in the response. No
x-litespeed-cache, nothing that said "cached." - The
Dateheader updated on every request, so it looked fresh. - Adding a query string (
?v=123) didn't change anything. - Being logged in to WordPress didn't bypass it.
- Turning on the host's development mode didn't help either.
We spent about 30 minutes chasing a phantom "Elementor is rejecting my widgets" bug. We even changed widget IDs. The writes had been fine all along.
The fix: purge the cache at the hosting level (in hPanel, or through the Hostinger API's clear-cache call), wait a few seconds, then fetch again.
The rule we follow now: never decide a write failed because the page looks unchanged. Read the data back from the database first. If the data is right and the page is wrong, it's a cache.
Cause 2: Your style settings do nothing
The symptom: text, spacing or colors ignore what you set in the widget JSON. On a batch of blog posts, paragraphs ran together with no gap between them, while bullet lists in the same posts were spaced correctly.
Here's what's happening. Elementor doesn't apply widget style settings (font size, line height, spacing, colors) directly. It compiles them into a CSS file for each post, at wp-content/uploads/elementor/css/post-<ID>.css. That file is built when someone saves the page in the editor and views it.
If you write _elementor_data straight into the database, nobody ever saves the page in the editor. No CSS file is built. Every style setting in your JSON is silently ignored, and the theme defaults win. Drafts are worse, because nobody views them publicly either.
How we proved it: the CSS files for our code-pushed posts returned 404. On the same site, pages saved in the editor each loaded a real post-<ID>.css. That comparison matters. A 404 alone isn't proof, because Elementor can also be set to print CSS inline instead of in files.
It also explained the lists. They looked right because their spacing came from a <style> block we had put inside the post content. The paragraphs relied on widget settings, which were never compiled.
The fix: don't rely on widget style settings for pages you push by code. Put the styles that matter in a <style> block inside an HTML widget, scoped to a class you control:
<style>
.my-post-body p { margin: 0 0 1.2em !important; line-height: 1.7 !important; }
.my-post-body a { color: #1a5fb4; text-decoration: underline; }
</style>Set that class on the section through the _css_classes setting, and the content styles itself whether or not Elementor ever builds a CSS file.
Cause 3: Your video embeds disappear
The symptom: you push a post with a YouTube embed. The post has an empty box where the video should be.
This one deleted every video across 30 blog drafts before we caught it.
WordPress has a security filter (called KSES) that strips risky HTML from content saved by users who lack the unfiltered_html permission. If your code writes to the database with an API key instead of as a logged-in administrator, it usually lacks that permission. So WordPress removes every <iframe> on save.
The giveaway is what's left behind. The wrapper survives and only the iframe vanishes, so the stored content holds an empty ratio box:
<div style="position:relative;padding-bottom:56.25%"></div>Nothing errors. The only clue is that the stored content is smaller than what you sent. In our case it was 28,534 bytes sent and 26,606 bytes stored.
The fix: don't send raw iframes. Use Elementor's native video widget, which stores only the URL and builds the iframe when the page renders. There's nothing for WordPress to strip:
{
"widgetType": "video",
"settings": {
"video_type": "youtube",
"youtube_url": "https://www.youtube.com/watch?v=VIDEO_ID",
"aspect_ratio": "169",
"yt_privacy": "yes",
"lazy_load": "yes"
}
}After switching, we read back all 30 posts and counted 57 videos stored correctly.
One warning: sites differ. On one client's setup <style> tags survived the same kind of write, and on another they were stripped. Test on each site before you push a batch.
The habit that catches all three
Every one of these bugs passed the usual check, "did the API return success?" What catches them is reading the result back and counting the thing you care about:
- Changed text? Read the database value, not the live page, before blaming the write.
- Changed styles? Check the post's CSS file exists, or put the styles in the content.
- Added videos? Count the video widgets or iframes in the stored data.
Count the tags, not just the text. When WordPress strips a <style> tag, the CSS inside it can remain as plain text. A search for your CSS will "pass" while the style tag itself is gone.




