| name | distribb |
| description | Distribb is an SEO platform that handles keyword research, original data research, content publishing to WordPress/Webflow/Shopify, high-DR backlink exchange network, link building outreach playbooks, internal linking, social media repurposing and posting, Google Business Profile management (live reviews, public review replies, Google posts), and Microworkers campaign management. Use this skill when the user wants to create SEO-optimized articles, find keywords, get real backlinks from other businesses, run link building or backlink outreach campaigns, publish to their CMS, manage their content calendar, manage their Google Business Profile and its reviews, post to their connected social accounts, or manage Microworkers campaigns. |
| homepage | https://distribb.io |
| metadata | {"clawdbot":{"emoji":"🔍","requires":{"env":["DISTRIBB_API_KEY"]}}} |
FIRST TIME READING THIS SKILL? STOP. WALK THE USER THROUGH THIS BEFORE ANYTHING ELSE.
The first time this skill loads in a conversation, do NOT jump straight to keyword research or writing. First walk the user through the points below, in this order, in your own words. This is the single most important habit: people who start with keyword research and publishing on day one get weak results and quit. People who follow the process get results.
A. What Distribb is, in one breath
Distribb is your SEO platform. You (the AI agent) do the thinking and the writing. Distribb provides the infrastructure: real keyword data, a backlink exchange network of real businesses, Google Search Console analysis, CMS publishing (WordPress, Webflow, Shopify, Ghost, Wix, Notion, GoHighLevel, Framer, or any API webhook), a content calendar, social repurposing, and done-for-you distribution on the Accelerator plan. You bring your own AI model. Distribb does everything around the writing.
B. The proper SEO process (show this list to the user every first run)
Tell the user this is the order that works, and that you will guide them through it:
- Create your account and go through onboarding at https://distribb.io . Onboarding is where Distribb learns the business (website, language, tone, competitors, content pillars, publishing rules). Do not skip it. Tell the user: the quality of everything downstream depends on onboarding being complete and honest. See
references/onboarding-guide.md for exactly what onboarding asks and why.
- Connect the two things that matter most: the website (CMS) and Google Search Console. The CMS connection lets Distribb publish. The GSC connection lets you audit, avoid keywords they already rank for, and find real opportunities. Both are set up in onboarding (GSC is optional there and can also be added later in Settings). See
references/onboarding-guide.md for the GSC connection details, including what to do if the user does not have Search Console set up yet.
- Make sure there is a blog to publish to. Many sites have no blog or no blog index page. Without one, articles have nowhere to live. Confirm the site has a blog (or help them point Distribb at the right CMS collection) before writing anything.
- Audit the site BEFORE writing a single article. This is step one of real SEO, not keyword research. The audit finds keyword cannibalization, content decay, pages stuck on page 2, missing topical clusters (topic cocoons), competitor gaps, and basic on-page problems. Run
/gsc-audit <domain> or follow references/audit-playbook.md. The audit is available on every plan.
- Plan topical clusters, then do keyword research. Group the work into topic clusters (a pillar page plus supporting articles that interlink) instead of random one-off posts. Then use keyword research to fill each cluster. See
references/audit-playbook.md (topical authority section).
- Write and publish, always feeding the backlink exchange. Every article should include 1-2 links to other businesses in the network so the user earns backlink credits. Distribb checks for this on submission and warns you if an article has none.
- Optimize what already ranks. Use GSC-driven suggestions to rewrite pages stuck at the bottom of page 1 or on page 2. This is the fastest win because the page already has authority. Run
/optimize.
This whole skill exists to run that loop for the user. When in doubt, point them back to it.
C. The Backlink Exchange (explain this clearly, it is the differentiator)
Distribb runs a network of real businesses that exchange backlinks. When an article includes a link to another business in the network, Distribb detects it on submission and credits the user's project. The more links the user gives, the more they receive. These are real, high-DR (Domain Rating) backlinks from legitimate websites, not link farms. Every current plan gets unlimited exchange access. Legacy Free Agentic accounts receive 1 backlink per month. Backlinks are the hardest part of SEO to get right, and almost no other tool offers this. See references/plans-and-backlinks.md.
D. Slash commands
This skill ships ready-to-use slash commands so the user can drive the whole workflow with /:
| Command | What it does |
|---|
/distribb | Overview, account status, and the proper SEO process above |
/distribb-setup | Check the API key, confirm website + GSC are connected, and enable the other slash commands |
/gsc-audit <domain> | Full SEO audit from Search Console + on-page + competitor + cannibalization + topic clusters |
/keyword-research <seed> | Keyword ideas with volume and difficulty |
/write-article <keyword> | Research, write, add internal links + backlinks, and publish one article |
/optimize | Find and rewrite pages stuck on page 2+ using GSC data |
/backlinks | Check backlink credits, see targets, and explain how the exchange works |
/content-calendar | List, schedule, and manage planned/draft/published articles |
/ai-visibility | Find where the user should be recommended by ChatGPT/Perplexity/Gemini and which listicles to pitch |
/news-writer <site-url-or-niche> | Newsjack: find fresh news in the niche, write grounded news drafts, and queue them in Distribb |
/statistics-page-writer <topic> | Deep-research and publish a sourced statistics page journalists cite for months |
/youtube-motion-video <topic> | Make a faceless motion-collage explainer video ("In a Nutshell" docu style), optimize it for YouTube SEO, and publish it to the connected YouTube channel |
/instagram-carousel <article-id-or-keyword> | Turn one article/keyword into a viral, save-driven Instagram carousel (cover hook, one idea per slide, comment-for-link play), publish it, and close the loop with a companion article |
/review-video <competitor> | Compile REAL, verified reviews of a competitor into a faceless " reviews" video, position the connected project's own business as the alternative, append the project's own testimonials, and publish to YouTube + a companion article |
/gbp | Google Business Profile manager: live review triage, draft + post public review replies, queue Google Business posts, post analytics |
If these commands are not yet available when the user types them, run /distribb-setup (or copy this skill's commands/*.md into the project's .claude/commands/ folder) to register them. See the Slash Commands section below.
E. Getting an account
If the user has no Distribb account yet, send them to https://distribb.io to sign up and go through onboarding. Their Distribb API key appears in Settings afterward. Plans at a glance (full detail in references/plans-and-backlinks.md):
- Agentic Mode ($49/mo, 3-day free trial): Distribb-provided keyword data, full backlink exchange.
- Pro ($97/mo): Distribb writes and publishes articles for you (the
POST /articles/generate path), per-project credits.
- Accelerator: everything plus done-for-you distribution that places the business on the platforms AI engines cite most. See section below and
references/plans-and-backlinks.md.
The free Agentic plan ($0/mo) is deprecated and no longer offered to new users. Current plans are Agentic Mode at $49/month and Pro at $97/month.
Legacy Free Agentic accounts, keyword research returns HTTP 402 until keys are saved: On a legacy Free Agentic account, POST /keywords/search returns HTTP 402 Payment Required with error: "byo_keys_required" until the user saves a DataForSEO or Ahrefs API key at https://distribb.io/settings#seo-keys. The 402 body includes an instructions_for_agent string. Surface it verbatim to the user, do not retry. See the Keyword Research, BYO Keys section below for the full contract.
Setup
export DISTRIBB_API_KEY=your_api_key_here
No installation required. All commands use curl and jq.
| Property | Value |
|---|
| name | distribb |
| description | SEO platform: keyword research, article writing, backlink exchange network, CMS publishing, social media repurposing, content calendar |
| allowed-tools | Bash(curl:), Bash(jq:), Bash(cat:*), WebFetch, WebSearch, Read, Write |
API Base URL
All endpoints use: https://distribb.io/api/v1
All requests require the header: Authorization: Bearer $DISTRIBB_API_KEY
Validate Your API Key
Before running any workflow, verify your API key works:
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/projects | jq .
If you get {"error": "Missing or invalid API key..."} or {"error": "Account is not active."}, the key is wrong or the account is inactive. Ask the user to check their API key in Settings at https://distribb.io/settings.
What You Get
| Capability | How It Works | Endpoint |
|---|
| Generate Article | Submit source content, Distribb AI expands into full SEO article (Pro plan only) | POST /articles/generate |
| Keyword Research | Search volume, difficulty scores, keyword ideas. Current plans use Distribb data; legacy Free Agentic accounts use the user's own DataForSEO or Ahrefs key (returns HTTP 402 if not set) | POST /keywords/search (alias: POST /keywords/research) |
| Backlink Exchange | Get real backlinks from other businesses in the network | GET /backlink-targets |
| Backlink Ledger | Full link-level detail behind the aggregate status: earned + scheduled links (source domain, DR, business, target URL, status, date) plus a velocity/gap summary | GET /backlinks |
| CMS Publishing | Publish to WordPress, Webflow, Shopify, Ghost, custom API | POST /articles/:id/publish |
| Content Calendar | Schedule articles, track status, manage your pipeline | GET /articles, POST /articles, PUT /articles/:id, DELETE /articles/:id |
| Feature Image | Attach a hero image URL to an article (cannot generate one; supply the URL) | POST /articles, PUT /articles/:id with feature_image |
| Project Settings | Read & edit the FULL settings surface (~30 fields): instructions, sitemap/blog URLs, content pillars, tone, writing profile, positioning, images/brand, competitors, toggles, publish time/timezone | GET /projects/:id, PUT /projects/:id |
| Create + Onboard Project | Create a new project (gated to paid slots; returns a buy-a-slot link if over) and optionally start keyword research + first articles. Ask the user before running research. Connect WordPress via API too | POST /projects, POST /projects/:id/onboarding, POST /projects/:id/wordpress |
Start Here: Onboarding and the Two Connections
Before any keyword research or writing, the user must have completed onboarding at https://distribb.io and connected two things. Everything downstream depends on this.
What onboarding collects (so you know what Distribb already knows, and what to fill if it is thin): the website URL, then an AI pass that auto-populates business details. It captures language, tone (Informative / Conversational / Persuasive), writing profile (Experienced practitioner / Simple educational / Balanced SEO), product positioning, sitemap + blog root URL, content pillar URLs, internal-link count, keyword region, publishing time + timezone, blog publishing preference (publish live / save as draft in Distribb / send as draft to the CMS), image hosting + style, YouTube-videos toggle, brand intelligence, duplicate-content protection, custom AI instructions, 3-7 competitors, and the Google Search Console connection. Full field-by-field detail and how to read or change each via the API is in references/onboarding-guide.md.
The two connections that matter most:
- Website / CMS (WordPress, Webflow, Shopify, Ghost, Wix, Notion, GoHighLevel, Framer, or API webhook). This is how Distribb publishes. Check it with
GET /integrations.
- Google Search Console. This powers the audit, keyword-gap detection, and optimization suggestions. Check it with
GET /search-console (returns connected: true/false). If the user has GSC access but has not connected it to Distribb, send them to https://distribb.io/integrations . If the user does not have Search Console set up at all yet, point them to Google's guide first: https://support.google.com/webmasters/answer/10267942?hl=en , then have them connect it in Distribb.
Also confirm the site actually has a blog to publish to. A site with no blog index has nowhere for articles to live, and this is a common reason new users see no results.
The SEO Audit (Run This First)
Real SEO starts with an audit, not with publishing. Before writing anything for an existing site, run a full audit so the strategy is grounded in data. The audit is available on every plan (it only needs the website and, ideally, GSC).
A Distribb audit covers:
- Keyword cannibalization (multiple pages competing for the same query)
- Content decay (pages losing traffic vs the previous period)
- Quick wins / striking distance (queries at positions 11-20 and pages stuck on page 2+)
- CTR optimization (pages that rank but get fewer clicks than expected)
- Dead pages (pages that dropped to zero traffic)
- Brand vs non-brand health
- Topical authority clusters (topic cocoons) (which pillars and supporting clusters to build)
- Competitor analysis (gaps vs the competitors captured in onboarding)
- Basic on-page checks (titles, meta descriptions, headings, internal linking, indexability)
Run it with /gsc-audit <domain> or follow the full playbook in references/audit-playbook.md. The audit pulls real data from GET /search-console, GET /suggestions, and GET /business-context, crawls the live site for on-page checks, and ends with a prioritized action list wired to Distribb (new articles for gaps, optimization suggestions for page-2 pages). For very large GSC datasets, run each analysis in its own sub-agent so context stays manageable.
Platform Tour: Where Things Live
Users often ask "where do I see X?" Here is the map (full detail in references/platform-guide.md):
| Page | What the user does there | API equivalent you can use |
|---|
| Dashboard | Overview of projects and recent activity | GET /projects |
| Content Calendar | See and manage planned / draft / published articles and their schedule | GET /articles, POST /articles, PUT /articles/:id, DELETE /articles/:id |
| Settings | Business description, custom AI instructions, publish time, timezone, backlink-network toggle, SEO data keys | GET /projects/:id, PUT /projects/:id |
| Integrations | Connect CMS, social accounts, Google Search Console, and Google Business Profile | GET /integrations, GET /search-console, GET /gbp/status, GET /social/accounts |
| Backlinks | See backlinks earned and given, and credits (aggregate) or the full link-by-link ledger | GET /backlinks/status, GET /backlinks, GET /backlink-targets |
| Optimizations / Suggestions | Review and approve GSC-driven rewrites of underperforming pages | GET /suggestions, POST /suggestions/run, approve/publish |
Yes, you (the agent) can check backlinks for the user. Use GET /backlinks/status?project_id=... for credits and counts, GET /backlinks?project_id=... for the full link-by-link ledger (every earned and scheduled link), and GET /backlink-targets for who they can link to next. The dashboard Backlinks page shows the same data visually.
Plans, Backlink Exchange, and the Accelerator
Quick reference (full detail in references/plans-and-backlinks.md):
- Backlink exchange: Every current plan gets unlimited exchange access. Legacy Free Agentic accounts receive 1 backlink per month. Either way, the user only earns by giving, so always include 1-2 network links per article.
- Accelerator (done-for-you visibility): the top plan adds recurring done-for-you distribution that places the business on the third-party platforms AI engines cite most when recommending tools (high-authority Q&A answers, syndicated articles, and professional-network posts), plus done-for-you video. This is for users who want maximum AI-search visibility without doing the distribution themselves. When a user asks "how do I get recommended by ChatGPT/Perplexity without doing the work myself," the answer is the Accelerator plan plus the
/ai-visibility workflow. Direct them to https://distribb.io to upgrade.
Core Workflow
The full end-to-end process for creating a high-ranking SEO article:
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/projects | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/business-context?project_id=42" | jq .
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"keyword": "crm software", "project_id": 42}' \
https://distribb.io/api/v1/keywords/search | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/internal-links?project_id=42&keyword=crm+software" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/backlink-targets?project_id=42&keyword=crm+software" | jq .
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"keyword": "best crm for small business",
"title": "Best CRM for Small Business: 2026 Guide",
"content": "<nav class=\"table-of-contents\" aria-label=\"Table of contents\"><h3>Table of Contents</h3><ul><li><a href=\"#introduction\">Introduction</a></li></ul></nav><h2 id=\"introduction\">Introduction</h2><p>Your full HTML article here...</p>",
"meta_description": "Compare the best CRM tools for small business in 2026.",
"scheduled_date": "2026-04-01T09:00:00Z",
"status": "Planned"
}' \
https://distribb.io/api/v1/articles | jq .
curl -s -X POST -H \
https://distribb.io/api/v1/articles/123/publish | jq .
Mandatory Article HTML Contract and Safe Edit Protocol
These rules apply to every article-producing command, helper, and sub-skill. They are a publishing contract, not optional style advice.
content is inserted inside Distribb's existing .blog-content element. Submit an HTML fragment, never a complete document.
- Never include
<!DOCTYPE>, <html>, <head>, <body>, a page <h1>, document metadata/schema, page-level <style>, or executable <script>. Distribb owns the document shell, H1, metadata, schema, author block, sidebar, and CSS.
- Give every H2 a unique, stable, URL-safe
id. When there are two or more H2s, include exactly one <nav class="table-of-contents"> whose anchors resolve to the current IDs.
- Preserve existing wrappers, classes, IDs,
data-* attributes, tables, links, and embeds during edits.
- Wrap every YouTube iframe in
<div class="youtube-embed">; include a descriptive title, loading="lazy", allowfullscreen, and no fixed dimensions.
For every edit: GET and save the article as a rollback copy; patch its fetched Content; diff and validate the fragment/IDs/TOC/embeds; PUT only changed fields; GET and verify readback. Published articles keep their slug and status: the API freezes the slug and rejects a move back to Draft/Planned. Distribb-hosted posts are live from the database; when a PUT returns sync_required: true, call POST /api/v1/articles/:id/sync (or resend the PUT with "sync": true; the CLI flag is --sync). Never republish or create a replacement for an existing live article.
Before publishing, and again on the live URL, inspect desktop and mobile widths. Confirm the sidebar/TOC matches the real headings and every video fills the article column at 16:9. A non-2xx response or failed readback is failure.
Commands Reference
List Projects
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/projects | jq .
Response:
{
"projects": [
{
"ID": 42,
"BusinessName": "Acme Corp",
"WebsiteUrl": "https://acme.com",
"BusinessDescription": "...",
"Language": "English (US)",
"Status": "Active",
"BacklinkCredits": 10,
"BecklinksNetworkParticipation": "Yes",
"ArticlesPerDay": 1
}
]
}
IMPORTANT: Check the BecklinksNetworkParticipation field. If it is "Yes", this project is part of the backlink exchange network. You MUST call /backlink-targets before writing each article and include 1-2 target URLs in the content. This is how the user earns backlinks from other real businesses. Skipping this means the user gives nothing and receives nothing from the network.
Project Settings (Read & Edit): the FULL settings surface
GET returns a settings object; PUT accepts that same shape. So the loop is: GET, change the keys you want, PUT them back (read-modify-write). The PUT exposes the entire Settings UI, ~30 fields, not just a handful, so you can configure a project end-to-end without the dashboard. This is what makes agency-scale onboarding possible.
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/projects/42 | jq .settings
curl -s -X PUT -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sitemap_url": "https://acme.com/sitemap.xml",
"blog_root_url": "https://acme.com/blog",
"content_pillars": ["https://acme.com/crm", "https://acme.com/pricing"],
"internal_links_per_article": 3,
"tone": "Conversational",
"writing_profile": "Balanced SEO",
"product_positioning": "Soft mention",
"image_hosting": "CMS",
"brand_color": "#1d4ed8",
"cta_intensity": "Soft",
"competitors": ["https://competitor1.com", "https://competitor2.com"],
"brand_intelligence": true,
"duplicate_content_protection": true,
"timezone": "America/New_York"
}' \
https://distribb.io/api/v1/projects/42 | jq .
Writable fields (every key the settings object returns is writable; aliases in parentheses):
| Field | Meaning / allowed values |
|---|
ai_instructions | Custom writing guidelines applied to every article. |
business_name, business_description | Brand name + what the business does (writing context). |
target_audience | List of audience strings, e.g. ["SaaS founders"]. |
sitemap_url | Sitemap URL (used to build the internal-link index). |
blog_root_url | Blog root URL. |
content_pillars | List of URLs (drives topic clusters + internal links). Each must be a valid URL, no spaces. |
internal_links_per_article (internal_links) | Integer 1 to 5. |
tone | Informative | Conversational | Persuasive. |
language | UI label or code, e.g. English (US), French, en-gb. |
keyword_region | e.g. United States, United Kingdom, Worldwide. |
writing_profile | Experienced practitioner | Simple educational | Balanced SEO. |
product_positioning | Neutral operational | Soft mention | Promotional. |
custom_author_name | Byline author name. |
social_media_ai_instructions | Custom instructions for repurposed social posts. |
publish_time | Daily auto-publish time, 24-hour "HH:MM". |
|
Partial updates are safe. The article-quality and image preferences are stored as merged JSON, so sending just {"cta_intensity": "Soft"} changes ONLY that, the other quality flags keep their current values. Invalid enum values return 400 with a message naming the allowed values.
Response (200):
{
"project_id": 42,
"updated_fields": ["tone", "cta_intensity", "competitors"],
"updated_columns": ["ContentStyle", "ArticleQualitySettings", "CompetitorWebsites"],
"message": "Project settings updated."
}
Not settable via API:
articles_per_day is plan-controlled. If sent, it's echoed back under ignored. Read it via GET /api/v1/projects or the settings block.
- Optimization thresholds (
min_position, max_position, min_impressions_per_week, min_article_age_days, excluded_article_ids) are applied at scan time by POST /api/v1/suggestions/run, not yet persisted per project. Sent values are echoed under ignored.
Create a Project (agency-scale onboarding)
Spin up a brand-new project for a client and configure it in ONE call. You can pass any writable settings field from the table above alongside the basics.
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"website_url": "https://client.com",
"business_name": "Client Co",
"business_description": "Bookkeeping for trades businesses.",
"target_audience": ["plumbers", "electricians"],
"tone": "Conversational",
"content_pillars": ["https://client.com/bookkeeping", "https://client.com/payroll"],
"competitors": ["https://rival.com"]
}' \
https://distribb.io/api/v1/projects | jq .
Response (201): { "project_id": 77, "project_slots": {"used": 3, "total": 5}, "next_step": "...", ... }
Project slots are gated by the paid quantity. If the account is already at its limit, you get HTTP 402:
{
"error": "project_limit_reached",
"active_projects": 5,
"project_quantity": 5,
"purchase_url": "https://distribb.io/dashboard?add_project=1",
"instructions_for_agent": "Tell the user they've hit their project limit and share purchase_url ..."
}
When you see this: show the user purchase_url (one click opens the "Buy More Seats" dialog in their dashboard). Once they confirm they bought a slot, retry the same POST. Never try to bypass the limit.
Creating a project does NOT start keyword research (that spends credits). After it's created, ASK the user whether they want to kick off keyword research + the first articles now. If yes, call the onboarding endpoint below.
Run Onboarding (keyword research + first articles)
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/projects/77/onboarding | jq .
Starts the same pipeline the dashboard runs when onboarding finishes: GSC-aware keyword discovery -> a planned content calendar -> the first articles begin generating. Returns 202; poll GET /api/v1/articles?project_id=77 to watch planned articles appear.
- Always ask the user first, this spends keyword/LLM credits.
- If the project already has articles, it returns
already_onboarded and does nothing.
- On Agentic plans and on legacy Free Agentic accounts this returns
skipped_byok (those plans bring their own keywords, use POST /api/v1/keywords/search then POST /api/v1/articles).
Connect WordPress
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "wordpress_url": "https://client.com", "integration_key": "<plugin Integration Key>" }' \
https://distribb.io/api/v1/projects/77/wordpress | jq .
Install the Distribb WordPress plugin on the site, copy its Integration Key, and send it here. Credentials are validated (format check + live probe) before saving, the same checks the dashboard runs. Returns { "status": "connected" }; if live validation is inconclusive (WAF/network) it still saves and returns a warning.
Business Context
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/business-context?project_id=42" | jq .
Response:
{
"business_name": "Acme Corp",
"website_url": "https://acme.com",
"description": "CRM platform for startups...",
"competitors": ["https://competitor1.com", "https://competitor2.com"],
"ai_instructions": "Use a friendly tone, focus on SaaS...",
"language": "English (US)",
"target_audience": "SaaS founders, startup CTOs",
"internal_links_per_article": 5
}
Use this before writing. The competitors list tells you which domains to NEVER link to. The ai_instructions field has custom writing guidelines from the user.
Keyword Research
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"keyword": "project management", "project_id": 42}' \
https://distribb.io/api/v1/keywords/search | jq .
Response (200 OK):
{
"keywords": [
{
"keyword": "best project management tools",
"search_volume": 12000,
"keyword_difficulty": 35
}
]
}
Returns the seed keyword plus up to 20 related keywords with volume and difficulty.
BYO Keys, legacy Free Agentic accounts
The free Agentic plan is deprecated and no longer offered to new users, but existing accounts still run. If the calling user is on one of those legacy Free Agentic accounts and has not yet saved a DataForSEO or Ahrefs API key, this endpoint returns HTTP 402 Payment Required with a structured body so your agent knows exactly what to do. Current plans (Agentic Mode at $49/month, Pro at $97/month, and Accelerator) never see this response.
Response (402 Payment Required):
{
"error": "byo_keys_required",
"message": "Keyword research requires your own DataForSEO or Ahrefs API key.",
"plan": "Agentic Free",
"required": { "any_of": ["dataforseo", "ahrefs"] },
"setup_url": "https://distribb.io/settings#seo-keys",
"docs_url": "https://distribb.io/api-docs#byo-keys",
"instructions_for_agent": "Tell the user to add their DataForSEO Login + API Key (or Ahrefs API Key) at distribb.io/settings, then re-run keyword research."
}
Agent contract, what to do when you see this 402:
- Halt the keyword-research step. Do not retry automatically.
- Surface the
instructions_for_agent string verbatim to the human user.
- Link the user to
setup_url (Distribb Settings → SEO Data API Keys).
- Resume keyword research only after the user confirms they've saved keys.
Pseudocode:
resp = call_distribb("/api/v1/keywords/search", body)
if resp.status_code == 402 and resp.json().get("error") == "byo_keys_required":
instructions = resp.json()["instructions_for_agent"]
setup_url = resp.json()["setup_url"]
say_to_user(f"{instructions} Setup link: {setup_url}")
return
If the user has saved only an Ahrefs key (not DataForSEO), the response is sourced from Ahrefs Keywords Explorer and includes "source": "byo_ahrefs" plus a note field. All other endpoints in this skill (articles, integrations, backlinks, internal links) work normally on legacy Free Agentic accounts without any BYO keys.
Internal Links
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/internal-links?project_id=42&keyword=crm+software" | jq .
Response:
{
"links": [
{
"url": "https://acme.com/blog/crm-guide",
"title": "The Complete CRM Guide",
"keyword": "crm guide",
"meta_description": "Everything you need..."
}
],
"num_links_recommended": 5,
"website_url": "https://acme.com"
}
Include the recommended number of internal links in each article. Place them naturally in the middle of paragraphs using <a href="EXACT_URL">descriptive anchor text</a>. Never use "click here". Space links at least 2 paragraphs apart.
Backlink Exchange
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/backlink-targets?project_id=42&keyword=crm+software" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/backlinks/status?project_id=42" | jq .
Targets response:
{
"targets": [
{
"url": "https://partner-site.com/related-article",
"title": "Related Partner Article",
"meta_description": "...",
"project_name": "Partner Co"
}
],
"category": "saas",
"credits": 10,
"instructions": "Include 1-2 of these URLs as natural references..."
}
How the backlink exchange works:
Distribb connects real businesses that exchange backlinks with each other. When you include a link to a network partner in your article, Distribb detects it on submission and credits the user's project. The more backlinks the user gives out, the more they receive in return. These are high-quality, high-DR backlinks from real business websites.
The category field shows how the keyword was classified (e.g. "saas", "ecommerce"). Targets are capped at 5 per request. Include 1-2 backlink targets per article as natural references. Do NOT fabricate information about linked sites. Use topically relevant anchor text.
Backlink Ledger (full link-level detail)
GET /backlinks/status gives the aggregate (credits + counts). GET /backlinks gives the link-by-link ledger behind it: every earned (verified) link and every scheduled/upcoming link, with the source domain, source Domain Rating, source business name, the target URL on the user's own site, status, and date, plus a velocity + competitor-gap summary. Use it to answer "which sites actually link to me, and how strong are they?"
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/backlinks?project_id=42" | jq .
Query parameters: project_id (required), earned_limit (default 100, max 500), scheduled_limit (default 50, max 200).
Response (200):
{
"project_id": 42,
"earned": [
{
"id": 55123,
"source_domain": "partner-site.com",
"source_business": "Partner Co",
"source_dr": 61,
"source_url": "https://partner-site.com/related-article",
"target_url": "https://acme.com/blog/crm-guide",
"status": "Verified",
"date": "2026-06-14"
}
],
"earned_count": 1,
"scheduled": [
{
"id": 55130,
"source_domain":
Scheduled links usually have no source_url yet (the linking article is still being written), so only source_domain/source_business/source_dr are populated for them. Per-link anchor text is not tracked yet, so this endpoint intentionally does NOT return an anchor-text-mix breakdown. Do not infer or fabricate one.
Generate Article (Pro plan only)
If the user wants Distribb to write the article from their source content (notes, drafts, talking points), use this endpoint. Distribb's AI will expand it into a full SEO article with YouTube videos, images, quotes, backlinks, and internal links. Costs 1 article credit. Not available on the Agentic plan.
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"keyword": "link building strategies",
"source_content": "Link building is about getting other websites to link to yours. Three main approaches: guest posting, broken link building, and creating linkable assets like original research...",
"instructions": "Add YouTube videos, include data and statistics",
"article_style": "Informative"
}' \
https://distribb.io/api/v1/articles/generate | jq .
Response (202):
{
"article_id": 456,
"status": "generating",
"keyword": "link building strategies",
"slug": "link-building-strategies",
"message": "Article generation started...",
"article_credits_remaining": 29
}
The article takes a few minutes to generate. Poll GET /api/v1/articles/456 to check when Status changes from Planned to Draft or Published.
Create Article
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"keyword": "best crm tools for startups",
"title": "10 Best CRM Tools for Startups in 2026",
"content": "<nav class=\"table-of-contents\" aria-label=\"Table of contents\"><h3>Table of Contents</h3><ul><li><a href=\"#introduction\">Introduction</a></li></ul></nav><h2 id=\"introduction\">Introduction</h2><p>Finding the right CRM...</p>",
"meta_description": "Compare the 10 best CRM tools for startups.",
"feature_image": "https://cdn.example.com/crm-comparison-hero.png",
"alt_text": "Comparison chart of the 10 best CRM tools for startups",
"scheduled_date": "2026-04-01T09:00:00Z",
"status": "Planned"
}' \
https://distribb.io/api/v1/articles | jq .
Response (201):
{
"article_id": 123,
"status": "Planned",
"keyword": "best crm tools for startups",
"slug": "best-crm-tools-for-startups",
"message": "Article created as Planned.",
"backlinks_processed": 2
}
If the article contained NO network backlinks, the response includes a warning:
{
"article_id": 124,
"status": "Draft",
"keyword": "crm for freelancers",
"slug": "crm-for-freelancers",
"message": "Article created as Draft.",
"backlinks_processed": 0,
"backlinks_warning": "Your project participates in the backlinks network but this article contains no backlinks to other network members. Include backlink targets (from GET /api/v1/backlink-targets) to earn credits and keep receiving backlinks."
}
IMPORTANT: If backlinks_warning is present in the response:
- Call
GET /backlink-targets to fetch network URLs for the article's keyword.
- Revise the article content to naturally include 1-2 of those URLs.
- Call
PUT /api/v1/articles/{article_id} with the revised content.
- If the user has disabled automatic revision, inform them: "This article doesn't include any backlinks to the exchange network. You won't earn backlink credits for it, which means fewer backlinks from other businesses."
Feature image
feature_image is an absolute http(s) URL, stored as the article's hero. Send
alt_text with it; if you omit it, the title is used. Both fields also work on
PUT /api/v1/articles/{id}, where "feature_image": "" clears the hero.
Distribb cannot generate an image for you through this API. The writer pipeline
that normally produces a hero does not run on content you submit yourself, so an
article you create here has no feature image unless you supply one. Generating a new
image is a dashboard action.
IMPORTANT: If image_warning is present in the response, the article has no hero:
- If
image_candidates is also present, those are absolute image URLs already
embedded in the article body. Promote the best one:
PUT /api/v1/articles/{article_id} with {"feature_image": "<url>"}.
- Do not blindly take the first candidate. In a listicle the early images are
usually screenshots of the competitors being reviewed, and a competitor's product
shot makes a poor hero for the user's own article. Pick one that represents the
user's brand, or a neutral/illustrative one.
- If there are no candidates, ask the user for an image URL rather than leaving it
blank silently.
Without a hero the article loses its og:image (no preview card when the link is
shared), its schema.org image, its on-page hero, and its thumbnail on the blog
index. Medium and LinkedIn syndication read the same field.
For long articles, write the HTML to a file and use @ syntax:
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content "$(cat article.html)" '{
"project_id": 42,
"keyword": "best crm tools",
"title": "10 Best CRM Tools",
"content": $content,
"status": "Draft"
}')" \
https://distribb.io/api/v1/articles | jq .
Setting a scheduled_date schedules the article: submit it as a Draft with a date and Distribb auto-promotes it to Planned (passing status: Planned yourself is still fine). Omit the date and it stays a Draft for review. What happens ON the date depends on the project's PublishingStatus (read it from GET /api/v1/projects): Publish Immediately goes live; Save as Drafts keeps it as a draft inside Distribb for manual review; Send as Drafts pushes it to the CMS as a draft. So a correctly-scheduled article on a Save as Drafts project will NOT auto-publish to the live site, that is by design, not a bug.
Update Article
Use this to revise an article after submission, including a Published article. Follow the mandatory fetch-patch-verify protocol above: GET and preserve the current article, patch its fetched Content, and send only changed fields.
curl -s -X PUT -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content "$(cat revised-article.html)" '{
"content": $content
}')" \
https://distribb.io/api/v1/articles/123 | jq .
Updatable fields: title, content, meta_description, keyword, article_style, status (Draft or Planned), scheduled_date, category, published_at, sync. Send only the fields you want to change. On a Published article the slug is frozen (changing keyword updates the keyword but no longer moves the URL) and status cannot go back to Draft/Planned, so the live URL and publication state cannot drift.
category: the CMS category NAME to assign (e.g. "Accessibility Guides"). It must ALREADY exist on the destination CMS (WordPress or GoHighLevel); Distribb resolves the name to that platform's category at publish time and cannot create new categories. Send "" to clear it. Detection ships for WordPress and GoHighLevel; other CMSs ignore it for now.
published_at: a PAST ISO 8601 timestamp used to BACKDATE the post on the CMS (e.g. "2024-02-05T09:00:00Z"). This changes only the date the CMS records, NOT when Distribb publishes and NOT the article's position on the content calendar (that is scheduled_date). Send ""/null to clear it. Backdating is applied on GoHighLevel today.
Response (200):
{
"article_id": 123,
"updated_fields": ["Content", "IsPreGenerated"],
"message": "Article updated successfully.",
"backlinks_processed": 2,
"sync_required": false
}
If content is updated and the project participates in the backlink network, Distribb re-scans for network backlinks and updates credits. GET and validate readback after the PUT. If the response says sync_required: true, call POST /api/v1/articles/:id/sync; this overwrites the existing CMS post and never creates a replacement. Distribb-hosted posts return sync_required: false and are live immediately.
Update a Published Article (edit content that is already live)
Published articles ARE editable. The edit lands inside Distribb first; the live post on the site changes only when you sync it. That two-step split means a typo can never go live by accident.
curl -s -X PUT -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content "$(cat corrected-article.html)" '{"content": $content}')" \
https://distribb.io/api/v1/articles/123 | jq .
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/articles/123/sync | jq .
sync_required tells you whether a push is even needed. Every PUT response carries it:
false on an unpublished article (nothing is live yet).
false on a Distribb-hosted post (distribb.io serves /blog/<slug> straight from the database, so the edit is live the moment it saves).
true when a live post exists on an external CMS. That is the only case that needs a sync.
Or do both in one call with "sync": true:
curl -s -X PUT -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "The Corrected Title", "sync": true}' \
https://distribb.io/api/v1/articles/123 | jq .
Two fields are frozen once an article is live:
keyword still updates the main keyword, but it no longer regenerates the slug. The slug IS the public URL and the key Distribb matches the remote post on, so changing it would 404 the live page and make the next sync create a second post.
status cannot go back to Draft or Planned (returns 400 cannot_unpublish). That would orphan the live post and let the scheduler publish a duplicate over the top of it. To take a post down, unpublish it from the Distribb dashboard or delete it in the CMS.
Sync outcomes:
| Response | What it means |
|---|
200 {"status":"synced","url":...} | The live post now carries your edit. |
400 {"error":"not_published"} | Nothing live to update yet. Use POST /api/v1/articles/<id>/publish first. |
400 {"error":"no_cms_integration"} | The site is disconnected. Reconnect it at https://distribb.io/integrations . |
400 {"error":"unsupported_for_sync"} | That platform has no update path yet. Edit the post directly on the platform. |
400 {"error":"sync_failed"} | The CMS rejected the update; message carries the platform's reason. The Distribb-side edit is still saved, so fix the cause and retry the sync. |
In-place updates are supported on: WordPress, API Webhook, Shopify, Webflow, Wix, Ghost, GoHighLevel, Framer, Notion. Anything else returns unsupported_for_sync.
Syncing is safe to repeat. Distribb finds the remote post by its stored ID, then slug, then title, and fails closed rather than risk publishing a duplicate.
CLI:
python distribb_cli.py articles:update --article-id 123 --content-file corrected.html --sync
python distribb_cli.py articles:sync --article-id 123
Delete Article
curl -s -X DELETE -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/articles/123 | jq .
Deletes a Draft or Planned article. Published articles cannot be deleted (the live CMS post would be orphaned), you get a 400. Unpublish or hide it from the dashboard/CMS first, or simply unschedule it.
Response (200):
{ "article_id": 123, "deleted": true, "message": "Article deleted." }
To take an article off the calendar without deleting it, unschedule instead: PUT /api/v1/articles/123 with body {"scheduled_date": null}.
List Articles
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/articles?project_id=42" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/articles?project_id=42&status=Published" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/articles?project_id=42&limit=20&offset=40" | jq .
Query parameters: project_id (optional), status (optional: Draft, Planned, Published), limit (default 50, max 200), offset (default 0).
Get Single Article
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/articles/123 | jq .
Publish Article
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
https://distribb.io/api/v1/articles/123/publish | jq .
Pushes the article to the user's connected CMS (WordPress, Webflow, Shopify, etc.). A manual publish like this always goes live, even if the project is set to Save as Drafts / Send as Drafts, that preference only controls AUTOMATIC scheduled publishing, not a deliberate "publish now". Returns 200 with {"status":"published","url":...} once the CMS confirms a live URL; 202 with {"status":"pending"} if the CMS hasn't confirmed yet (it will retry, the article is NOT lost).
Use this only for Draft/Planned articles without a live post. For a Published article, PUT and then use the update-only /sync endpoint when requested; never publish it again.
The project must have a website/CMS connected. If none is, this returns 400 with {"error":"no_cms_integration"} and a connect_url. Surface that to the user verbatim, there is nowhere to publish until they connect their site at https://distribb.io/integrations . This is the single most common reason a scheduled article never publishes: a Google Search Console (analytics) connection is NOT a publishing destination. Before you tell a user an article will publish, confirm a CMS is connected with GET /api/v1/integrations.
Social Media Repurposing (Automatic)
When an article is published to the user's CMS, Distribb automatically generates social media posts for every platform the user has connected (X/Twitter, LinkedIn, Reddit, Facebook, Instagram, etc.). The agent does not need to call any endpoint for this. It happens server-side.
The social posts are created as drafts in the user's content calendar so they can review, edit, or schedule them from the Distribb dashboard. If the user has connected social accounts, publishing an article through the API triggers this automatically.
Posting to Social Media Yourself
Repurposing is automatic, but you can also write a post and send it to the user's connected accounts. This is the same publisher behind the dashboard's Social Composer, so anything you send lands in their calendar next to everything else.
1. See what is connected.
curl -s "https://distribb.io/api/v1/social/accounts?project_id=42" \
-H "Authorization: Bearer $DISTRIBB_API_KEY" | jq
Returns {connected, accounts: [{platform, account_id, account_name}], instructions_for_agent}. A project with nothing connected is not an error: it returns 200 with connected: false. Connecting an account is a browser OAuth step at https://distribb.io/integrations, so you cannot do it for the user.
2. Post.
curl -s -X POST https://distribb.io/api/v1/social/publish \
-H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"content": "The three keyword mistakes that cost us six months.",
"platforms": ["linkedin", "x"],
"link": "https://example.com/blog/keyword-mistakes"
}' | jq
| Field | Notes |
|---|
project_id | Required. Must be a project the API key's account owns or is a team member of. |
content | Required. The post text. |
platforms | Required. ["linkedin", "x"], or [{"platform": "x", "account_id": "..."}] when one platform has several connected accounts. |
link | Optional. Stored with the post. On LinkedIn and Facebook it is appended to the text so the preview card renders. |
media_files | Optional. [{"type": "image", "s3_url": "..."}]. |
platform_overrides | Optional. Per-platform copy and per-network options, keyed by platform. |
scheduled_for | Optional ISO8601 UTC. Schedules the post instead of sending it now. |
The CLI wraps both:
python distribb_cli.py social:accounts --project-id 42
python distribb_cli.py social:publish --project-id 42 --platforms linkedin,x \
--content "The three keyword mistakes that cost us six months." \
--link https://example.com/blog/keyword-mistakes
Supported platforms: x (or twitter), linkedin, facebook, instagram, threads, bluesky, reddit, tiktok, youtube, pinterest, telegram, snapchat, googlebusiness.
Per-platform copy beats one shared post. Use platform_overrides to rewrite for each network and to reach the options only that network has:
{
"platform_overrides": {
"x": {"threadSteps": ["Second tweet.", "Third tweet."]},
"linkedin": {"text": "A longer, first-person version.", "firstComment": "Full breakdown: https://example.com/blog/keyword-mistakes"},
"reddit": {"subredditName": "SEO", "title": "What six months of keyword mistakes taught us"}
}
}
Scheduling. With scheduled_for the post is saved as scheduled and goes out within five minutes of that time, and the user can still edit or delete it in the dashboard until then. The response is a 201 with scheduled: true. Without it the post publishes immediately and the response carries the live URLs.
Character limits are enforced server-side: X 280, Bluesky 300, Threads 500, Pinterest 500, Google Business 1500, Instagram and TikTok 2200, LinkedIn 3000, YouTube 5000. Going over is a 400 naming the platform and the count, so write to the limit rather than fixing it after a rejection.
Confirm the copy before you send it. A published post is public and immediate, and deleting it later does not undo who saw it. Show the user the exact text per platform and wait for a yes, unless they have already told you to post without checking.
YouTube SEO With Motion Videos (/youtube-motion-video)
Turn a keyword or concept into a short, faceless motion-collage explainer video
(bold screen-print cutout collage visuals, a calm "In a Nutshell" documentary voice),
optimize it for YouTube SEO with Distribb's real keyword + Search Console data, and
publish it to the user's connected YouTube channel through Distribb, then close the
loop with a companion article that embeds it.
This is a hybrid workflow: Distribb is the SEO brain (keyword data, GSC, internal links,
backlinks, the companion article) and the super-video-maker skill is the production
engine (OpenAI gpt-image-2 collage stills → Seedance 2.0 via fal.ai motion → ElevenLabs
narration → captions). Install it once with npx skills add Bomx/super-video-maker-skill.
Run it with /youtube-motion-video <topic> and follow references/youtube-motion-video-playbook.md
for the full method (preflight, the collage look, the Seedance motion rules, the docu
voice, the SEO packaging, and the connect-and-publish path). The user connects their own
YouTube channel first at https://distribb.io/integrations ("Connect via Google").
Instagram Carousels for SEO (/instagram-carousel)
Instagram carousels are an SEO tactic, not just social. Since mid-2025 public posts from
professional accounts are indexed by Google/Bing and cited by LLMs, and carousels manufacture
the strongest predictor of AI-engine citations for a brand: branded search. They also
carry the highest saves of any IG format (~9x a single image), and a save is the dominant
ranking signal for educational content.
This workflow re-architects one Distribb article (or one target keyword) into a viral,
save-driven carousel using our 2026 best practices: a cover that teases (it carries ~80% of
the outcome), an 8-slide swipe arc (cover -> promise -> one-idea-per-slide value -> recap ->
CTA), the exact design system (1080x1350, safe zones, >=4.5:1 contrast, consistent template),
keyword-rich captions + per-slide alt text (the indexable surfaces, since hashtags no
longer drive reach), and the comment-a-keyword-to-DM link play (one ALL-CAPS keyword +
misspelling variants + an auto-DM, which beats link-in-bio). It then closes the SEO loop with
a companion Distribb article on the same keyword so the attention becomes indexable owned
authority.
Run it with /instagram-carousel <article-id-or-keyword> and follow
references/instagram-carousel-playbook.md for the full method (the Carousel Maker JSON
contract, the cover/hook formulas, the design specs, how to render the slides, the
comment-for-link playbook, and the publish paths: Graph API, a scheduler, or a ready-to-post
hand-off). The user connects their Instagram professional account first at
https://distribb.io/integrations .
List Integrations
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/integrations?project_id=42" | jq .
Google Search Console
Pull the user's real search performance from Google Search Console, top queries, top pages, and site totals (clicks, impressions, CTR, average position). Use it to find queries worth targeting, pages sitting just off page 1, or terms the user already ranks for. Requires the user to have connected GSC.
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/search-console?project_id=42&days=28&limit=25" | jq .
Query parameters: project_id (required), days (default 28, max 90), limit (rows per list, default 25, max 1000), start_row (pagination offset, default 0), compare (true to add period-over-period deltas vs the immediately preceding window of equal length).
Aliases: GET /rankings and GET /analytics are documented aliases of this endpoint (same handler, same parameters and response). They serve Search-Console search performance (clicks, impressions, CTR, position by query and by page), NOT web-session analytics. If a user asks for "analytics" expecting sessions/bounce-rate/traffic-source data, clarify that Distribb exposes Search-Console performance, not a web-analytics product.
Pagination: page through large result sets with start_row. The response includes a pagination block with the current limit, start_row, and a next_start_row (null when there are no more rows). Fetch the next page by passing start_row=next_start_row.
Compare (compare=true): adds a comparison block with previous_date_range, previous_totals, and delta_totals, and adds delta_clicks, delta_impressions, delta_position, and is_new to each query/page row. Use it to spot rising or decaying queries and pages at a glance.
Response (200, connected):
{
"connected": true,
"project_id": 42,
"property": "sc-domain:acme.com",
"date_range": { "start_date": "2026-05-06", "end_date": "2026-06-03", "days": 28 },
"totals": { "clicks": 1840, "impressions": 92344, "ctr": 0.0199, "avg_position": 18.4 },
"top_queries": [
{ "query": "best crm for small business", "clicks"
Response (200, NOT connected):
{
"connected": false,
"message": "Google Search Console is not connected for this project.",
"instructions_for_agent": "Tell the user to connect Google Search Console at https://distribb.io/integrations ...",
"connect_url": "https://distribb.io/integrations"
}
Agent contract:
- If
connected is false, stop and tell the user the instructions_for_agent text verbatim, link them to connect_url (https://distribb.io/integrations), and do not retry until they confirm they've connected GSC.
- If
connected is true but the body has "error": "gsc_fetch_failed", their Google token likely expired, tell them to reconnect at the same URL.
How to use the data: queries with lots of impressions but low CTR or an average position of ~8-20 are the best targets, write a new article or refresh an existing one for them. Pages at the bottom of page 1 (position ~8-12) often just need internal links and a content refresh to climb. Pair this with POST /articles (write the piece) and GET /internal-links (cross-link it).
Google Business Profile (reviews, replies, posts, analytics)
Act on the user's connected Google Business Profile: read their Google reviews live, post or delete the business's public review replies, queue Google Business posts, and pull post analytics. Local-SEO leverage in one surface: reviews with owner replies convert better and fresher profiles rank better in the map pack. Requires the Google Business integration (Integrations page -> 'Add Integration' -> 'Google Business', signed in with the Google account that manages the profile).
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/gbp/status?project_id=42" | jq .
Response (200, connected):
{
"connected": true,
"business_name": "Fusion Electrical & Carpentry Projects",
"location_address": "11/23 Lake Road, Tuggerah, NSW",
"connected_at": "2026-07-10T01:46:44",
"review_notifications_subscribed": true,
"total_reviews": 96,
"average_rating": 5,
"unreplied_count": 50,
"unreplied_count_is_partial": true,
"capabilities": ["list Google reviews (live)", "reply to reviews / delete a reply", "publish Google Business posts (via the social pipeline)", "post-level analytics for posts published through Distribb"],
"not_available":
When connected is false the body carries instructions_for_agent, so relay it verbatim and stop until the user connects.
List reviews (live from Google):
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/gbp/reviews?project_id=42&has_reply=false&limit=25" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/gbp/reviews?project_id=42&max_rating=3" | jq .
Query parameters: project_id (required), has_reply (false = needs-reply triage), min_rating/max_rating (1-5), sort_by (date|rating), sort_order (asc|desc), limit (1-50, default 25), cursor (pass a previous response's next_cursor while has_more is true).
Each review: review_id, reviewer_name, rating, text, created, has_reply, reply_text, reply_created, review_url. The response's total_reviews / average_rating reflect the current filter (with has_reply=false, total_reviews is the unreplied total).
Reply to a review (PUBLIC, confirm wording with the user first):
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" -H "Content-Type: application/json" \
-d '{"project_id": 42, "review_id": "accounts/.../locations/.../reviews/AbFvOq...", "message": "Thanks Sarah, glad the switchboard upgrade went smoothly. See you on the next project!"}' \
https://distribb.io/api/v1/gbp/reviews/reply | jq .
curl -s -X DELETE -H "Authorization: Bearer $DISTRIBB_API_KEY" -H "Content-Type: application/json" \
-d '{"project_id": 42, "review_id": "accounts/.../locations/.../reviews/AbFvOq..."}' \
https://distribb.io/api/v1/gbp/reviews/reply | jq .
Reply etiquette the agent should follow: write in the project's brand voice, thank the reviewer by name, reference something specific from their review, keep it short (2-4 sentences, max 4000 chars). For negative reviews: stay professional, own what's ownable, move resolution offline ("call us at ..."). Never argue. To EDIT a live reply, just reply again with the new text (Google keeps one owner reply per review); to remove it, use DELETE.
Create a Google Business post:
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" -H "Content-Type: application/json" \
-d '{"project_id": 42, "text": "Spring switchboard-safety checks are now booking...", "link": "https://acme.com/switchboard-checks", "scheduled_date": "2026-07-15 09:00"}' \
https://distribb.io/api/v1/gbp/posts | jq .
Body: text (required, max 1500 chars), link (optional, becomes the post's Learn More button), image_url (optional, public http(s) image), scheduled_date (optional, YYYY-MM-DD or YYYY-MM-DD HH:MM UTC). With scheduled_date the post is scheduled and auto-publishes at that time; without it the post is saved as a draft for review in the Social Composer. Returns 201 with post_id + status. Articles published through Distribb also auto-repurpose to Google Business when the integration is connected, so use this endpoint for standalone posts (offers, updates, seasonal pushes).
Post analytics:
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/gbp/analytics?project_id=42" | jq .
Covers Google Business posts published through Distribb (post counts + per-post engagement where Google provides it).
Agent contract:
- Review counts, ratings, and review text must come only from these payloads, never estimate or invent them.
- Replies are public on Google immediately under the business name. Unless the user already supplied or approved the exact wording, show the draft and get a go-ahead before
POST /gbp/reviews/reply. Bulk-replying is fine once the user approves the approach and tone (e.g. "reply to all unreplied 5-star reviews, one line each, varied wording").
- This connection cannot read location insights (calls, direction requests, website clicks, search keywords), Q&A, photos, or edit business info. Say so when asked instead of promising them.
- A
404 with instructions_for_agent on the write endpoints means Google Business is not connected, so relay the instructions.
Good /gbp workflow: GET /gbp/status -> if unreplied_count > 0, pull has_reply=false reviews -> draft replies in the brand voice (get approval) -> post them -> finish with a queued Google Business post pointing at the latest published article.
Link Outreach (listicle backlink replies)
Link Outreach is Distribb's managed service that emails "best of" / "top tools" listicle authors, from Distribb's warmed inboxes, asking to add the user's business to lists that already rank their competitors. Discovery, sending, and follow-ups are automatic. This surface is for the replies: seeing which authors answered (and what they want) and responding to them in-thread, without the user leaving Distribb.
GET /link-outreach/prospects?project_id=...&status=replied,offer&limit=50 lists the authors who replied. Each row: prospect_id, host, author_name, author_email, status (replied or offer), the author's reply (their actual message), and offer_amount / offer_currency when they named an asking price. Omit project_id to span every project the key owns; status=all returns the whole pipeline. Read-only, 30 req/min.
POST /link-outreach/prospects/:id/reply with { "body": "<the reply>" } sends the user's reply in-thread from the Distribb inbox that ran the original outreach, so it stays connected and lands in the author's inbox. Accelerator only (other plans reply from their own inbox). 10 req/min.
Agent contract (this SENDS a real email on the user's behalf):
- Always
GET the replies first and summarize them. Quote the reply text and any offer_amount exactly. Never invent a price, terms, or intent the author did not state.
- Most listicle placements are paid; that is the industry norm. Whether to accept, negotiate, or pass is ALWAYS the user's call, never yours. Distribb never agrees to or pays for a placement on their behalf.
- Draft the reply, show the user the exact wording, and get a clear go-ahead BEFORE calling
POST .../reply. It is not reversible. Never send a reply the user has not seen and approved.
Good /link-outreach workflow: GET /link-outreach/prospects -> summarize who replied + any asking price -> draft a short reply in the user's voice -> get explicit approval of the wording -> POST /link-outreach/prospects/:id/reply.
AI Visibility (AEO)
Read Distribb's already-tracked AI-search visibility for a project: how often AI engines cite the site, the share-of-voice vs competitors, per-engine citation status, the tracked prompts, and the exact pages engines cited. This is the API-key mirror of the dashboard AI-visibility pane (same ai_citation_tracker backend), so an agent can pull it directly instead of re-deriving everything with live WebSearch. The five tracked engines are ChatGPT, Perplexity, Gemini, Google AI Overviews, and Google AI Mode.
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/ai-visibility?project_id=42&view=summary" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/ai-visibility?project_id=42&view=prompts&page=1&per_page=10" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/ai-visibility?project_id=42&view=competitors" | jq .
curl -s -H "Authorization: Bearer $DISTRIBB_API_KEY" \
"https://distribb.io/api/v1/ai-visibility?project_id=42&view=cited_pages" | jq .
Query parameters: project_id (required), view (summary default, or prompts | competitors | cited_pages). For view=prompts: page (default 1), per_page (default 10).
Summary response (200):
{
"has_data": true,
"scanning": false,
"score": 38,
"engines_citing": 2,
"engines_total": 5,
"share_of_voice": 12,
"prompts_tracked": 18,
"scan_location": "Sydney,New South Wales,Australia",
"engines": [
{ "key": "chatgpt", "label": "ChatGPT", "state": "cited" },
{ "key": "perplexity", "label":
Per-engine state is one of cited (your page was cited), mentioned (brand named but not cited), not_cited (the engine answered but did not cite you), or no_data. If has_data is false, no scan has run yet (or one is scanning), trigger one with the scan endpoint below. The competitors view returns you (your citation count), rows (each stored competitor with citation counts), and other_cited (most-cited domains that are neither you nor a competitor). The cited_pages view returns your own URLs that engines cited, with per-URL citation counts and which engines cited them.
Trigger a scan
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": 42}' \
https://distribb.io/api/v1/ai-visibility/scan | jq .
Queues an on-demand scan and returns 202 {"status":"queued", ...}. Poll GET /ai-visibility?view=summary until it completes. The per-project daily manual-scan cap is SHARED with the dashboard "Scan now" button and the Distribb Agent, so heavy API scanning draws from the same budget. When the cap is hit you get 429 {"status":"rate_limited", "manual_scans_used": N, "manual_scans_limit": M} (resets at midnight UTC). project_id may be sent in the body or as a query param.
Add or remove a tracked prompt
curl -s -X POST -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": 42, "prompt": "best crm for small business"}' \
https://distribb.io/api/v1/ai-visibility/prompts | jq .
curl -s -X DELETE -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": 42, "prompt": "best crm for small business"}' \
https://distribb.io/api/v1/ai-visibility/prompts | jq .
Body: {"project_id": <id>, "prompt": "..."}. Added prompts are picked up on the next scan. POST returns {"status":"added"|"duplicate"|"limit"|"invalid"} (a limit status means the tracked-prompt cap is reached, remove one first). DELETE returns {"status":"removed"} or 404 {"status":"not_found"}.
Bring your own prompts (the ones that matter). Every project seeds with about 10 auto-generated prompts that skew brand-y ("Is [brand] a good tool?"). Those are just a starting point. Add the real buyer queries your clients care about ("best pickleball paddle australia", "best crm for real estate") with the add endpoint, they are your highest-signal prompts and are scanned first. A project tracks up to 25 prompts total (your own prompts take priority over the auto ones); remove the auto prompts you do not want so your buyer queries fill the set. To lift the ceiling above 25 across the whole workspace, ask Distribb to raise AI_CITATION_MAX_TRACKED (it is cost-bounded: each prompt is scanned across 5 engines per scan).
Set the scan location (local businesses)
For local businesses, proximity decides the AI answer, so the scan should run from the client's own market, not a default country. Distribb scans Google AI Overview, Google AI Mode, ChatGPT, and Gemini from the project's primary_location (a "City, Region, Country" string) when it is set, and falls back to the country otherwise. Perplexity is always country-level (its API accepts only a country code). The location the scan actually used is echoed back as scan_location on the summary response, so you can prove localization in a report.
primary_location is captured at onboarding, and you can set or override it per client via the projects API (or the CLI):
curl -s -X PUT -H "Authorization: Bearer $DISTRIBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"primary_location": "Sydney, New South Wales, Australia"}' \
https://distribb.io/api/v1/projects/42 | jq .
If DataForSEO does not recognize the city, the scan quietly falls back to the country for that run (no error), so a rough or misspelled market never breaks a scan. Changing the location shifts where future scans measure from, so trend lines compare like-for-like only after the first scan at the new location.
CLI
The same surface is wrapped in distribb_cli.py so you do not have to hand-write curl:
python distribb_cli.py ai-visibility:get --project-id 42 --view summary
python distribb_cli.py ai-visibility:get --project-id 42 --view prompts --page 1 --per-page 25
python distribb_cli.py ai-visibility:get --project-id 42 --view competitors
python distribb_cli.py ai-visibility:get --project-id 42 --view cited_pages