A store blog that fills itself, one article at a time.
Rankli writes for the things you sell and creates each finished piece as an article in one of your store’s blogs, over the Admin API with a custom-app token scoped to write_content. Nothing else on the store is touched — no products, no themes, no customers, and no scope that would let us near them.
A Shopify article lives in a blog, so the first choice is which one.
A store can have more than one, and articles have to go into a particular one. Verification lists every blog on the store, and the connection remembers the one you picked.
Make a custom app with write_content.
Store admin → Settings → Apps and sales channels → Develop apps. The Admin API access token it issues is what we send, in an X-Shopify-Access-Token header. write_content is the only scope this needs; nothing we do requires read or write access to orders, customers or products.
Give us the store address.
Your your-store.myshopify.com host, or the custom domain in front of it — we take the host either way. The API path and the pinned version are ours to add.
Test it, and pick the blog.
Testing reads /blogs.json and hands back every blog on the store to choose from. Leave the choice blank and articles go to the store’s first blog. If the store has no blog, verification says so and tells you to create one under Online Store → Blog posts, rather than accepting a connection that would fail on its first article.
Reading blogs proves the token authenticates. It does not prove write_content — only writing can — so a token missing that scope verifies and then fails its first publish with a message naming the scope. We would rather say that here than pretend the check is stronger than it is.
The fields a Shopify article has, and what goes into each.
One POST /blogs/{blog_id}/articles.json. The mapping is worth reading, because two of these behave differently from every other platform we publish to.
| Shopify field | What we put in it |
|---|---|
| Title and handletitle, handle | The article title and its slug. The handle becomes the last segment of the public URL, under /blogs/{blog}/. |
| Body and summarybody_html, summary_html | The article as HTML, and the meta description in the summary field. |
| Publishedpublished | true or false, from the connection’s draft-or-live setting. False means the article exists in your admin and is not on the storefront. |
| Authorauthor | A display name, not an id — the opposite of WordPress, where the same setting is a numeric user id. Whatever you type is what appears on the article. |
| Tagstags | Shopify articles have no categories, so the connection’s category setting is sent as tags — the closest native construct — comma-separated. Nothing is sent when you have chosen none. |
| Featured imageimage { src, alt } | A URL for Shopify to fetch, plus its alt text. See below — this one has a condition on it. |
The blog id travels with the article.
A Shopify article’s address includes the blog it is in, so what we record against the article is {blog id}/{article id} rather than the article id alone. Changing which blog a connection points at therefore does not orphan anything already published: updates still find the article where it actually lives.
The featured image moves to Shopify’s CDN. Body images do not.
This is the sharpest difference between the Shopify integration and the WordPress one, and it is a property of the API rather than a decision we made.
The featured image is re-hosted.
image: { src, alt } hands Shopify a URL; Shopify fetches it and serves the file from its own CDN from then on. That is why this adapter has no upload call — the platform does the copying.
The condition: it has to be a URL Shopify can fetch. With no image provider configured, our illustrations are inline data URLs, and Shopify answers 422 for those — which would trade the whole article for its thumbnail. So the field is left off when there is nothing fetchable, and the article is created either way.
Images inside the body keep their original URLs.
The REST article resource offers no way to upload a body image, so in-body pictures are not copied onto your store. They stay wherever they are — which, in the default configuration, means inside the article HTML itself as inline data, so they render from your store with no dependency on us at all.
With an image provider configured they are external URLs, and an external URL can stop resolving. If that matters to you, the honest answer today is that Shopify body images are the one place in this product where an article’s pictures are not on your own infrastructure.
A pinned API version, and the failures worth recognising.
Pinned to 2025-07.
Shopify versions its Admin API, and the version is part of the path. We name one explicitly in every request rather than following the latest, so a Shopify release cannot change how your blog is written to without us noticing first.
Shopify marks the REST Admin API as legacy in favour of GraphQL. Articles remain fully supported on stable versions, which is why this adapter uses REST today; when that stops being true, the version above will change and this page will change with it.
When Shopify says no.
- 401. The access token was rotated, or the custom app was uninstalled.
- 403. The token is missing write_content. This is the one that appears on a first publish after a clean verification.
- 404. The store URL is wrong, or the article we were updating is gone. A deleted post is recreated rather than failing forever on an id nothing answers to.
- A blog that has been deleted is caught before any request is made, by name: “the configured Shopify blog (id …) no longer exists on this store”.
429s and 5xx are retried with backoff and an honoured Retry-After. A 4xx is misconfiguration and is never retried against your store.
What is not sent.
Metafields, Shopify’s own SEO title and description fields, a scheduled publish date, and any link between an article and a product or collection. The article carries the fields listed in the table above and nothing else. There is also no routing rule: every article from a connection goes to the one blog that connection names.
Four rules that do not depend on which platform you use.
Which blog, and what happens to images, are Shopify’s. These four are the pipeline’s, and they hold identically for a WordPress or Webflow connection.
Nothing publishes until the credentials verify.
A connection is saved unverified and is not used. Verification is a real call to your platform — it reports who we authenticated as and, where the platform will say, whether that identity may publish at all. Change the URL or any credential and the connection goes back to unverified until it is tested again.
Draft or live is your choice, per connection.
Draft is the default: articles arrive unpublished and nothing is public until someone presses publish on your side. Choosing live is a deliberate switch, and the screen says in plain words that nobody on your team will see the article first.
A publish that fails is reported as failed.
The failure is stored against the article with the reason the platform gave, and the job is marked failed rather than done. A 4xx is treated as misconfiguration and is not retried — retrying bad credentials against your site is how one wrong password becomes an IP ban.
An article that fails a hard SEO rule never reaches this stage.
The publish step is only queued for a draft that passed review and that a model actually wrote. An article held back says which rule held it. That gate sits before every adapter, so it applies to all four equally.
Straight answers.
Does this touch my products or my theme?
No. The only scope the connection needs is write_content, and the only endpoints it calls are the blog and article ones printed at the top of this page. It cannot read an order or edit a product because it has not been given the ability to.
Will the articles be about what I actually sell?
They are planned from your own site: Rankli reads it, builds a voice profile and plans a calendar you can edit before anything is written. That is upstream of this integration and applies whichever platform you publish to — the Shopify part is only the last step.
An article that fails a hard SEO rule, or that our fallback simulator wrote because a model was unavailable, is held back and never reaches your store.
Can I review each article before it appears on the storefront?
Yes, and that is the default. With the connection set to draft, articles are created with published: false — visible in your admin under the blog, invisible to shoppers until you publish them there.
Do you support Shopify’s new GraphQL Admin API?
Not today. The adapter uses the REST Admin API on a pinned version because blog articles remain fully supported there. That is a decision with an expiry date on it, and we are not going to describe a GraphQL implementation we have not written.
Connect a store blog and read the first three articles.
$1 once buys 3 finished articles, created unpublished in the blog you choose unless you say otherwise. Nothing renews.