GrowthAtlas Connector API v1
This document describes the GrowthAtlas Connector API v1 specification — the contract that all GrowthAtlas connectors (WordPress plugin, Laravel package, custom implementations) must implement.
GrowthAtlas uses this API to push content drafts to your website and to pull page/entity data for SEO analysis. As long as your site exposes these endpoints, GrowthAtlas can publish to it, regardless of platform.
Base URL
Connectors expose endpoints under a sub-path of your domain. The path prefix depends on the connector:
| Connector | Base path |
|---|---|
| WordPress plugin | /wp-json/growthatlas/v1/ |
| Laravel package | /api/growthatlas/v1/ |
| Custom connector | Configured by you in the dashboard |
Authentication
All requests from GrowthAtlas include a Bearer token:
Authorization: Bearer <your-api-key>
Optionally, connectors can verify an HMAC-SHA256 signature for additional security:
X-GrowthAtlas-Signature: sha256=<hmac>
The HMAC is computed as HMAC-SHA256(secret, request_body). The signing secret is configured in the connector settings and in the GrowthAtlas dashboard.
Request Headers
GrowthAtlas always sends the following headers:
| Header | Value |
|---|---|
Authorization |
Bearer <api-key> |
Content-Type |
application/json |
Accept |
application/json |
User-Agent |
GrowthAtlas/1.0 (+https://growthatlas.io) |
X-GrowthAtlas-Version |
v1 |
Idempotency-Key |
ga_draft_{draft_id}_{attempt} (on POST) |
Response Envelope
All responses must be JSON. Successful responses return HTTP 200 or 201. Error responses return 4xx or 5xx.
Success
{
"success": true,
"data": { ... }
}
Error
{
"success": false,
"message": "Human readable error message",
"errors": { "field": ["validation error"] }
}
Endpoints
GET /health
Health check. Returns connector version and platform info.
Response:
{
"success": true,
"data": {
"status": "ok",
"connector": "wordpress",
"connector_version": "1.0.0",
"platform": "wordpress",
"platform_version": "6.5.3",
"php_version": "8.2.10",
"growthatlas_api_version": "v1"
}
}
GET /site-profile
Returns basic information about the website for context in content generation.
Response:
{
"success": true,
"data": {
"name": "My Website",
"url": "https://example.com",
"description": "A brief description of the site",
"language": "en",
"platform": "wordpress",
"timezone": "UTC",
"post_types": ["post", "page", "product"],
"taxonomies": ["category", "tag"]
}
}
GET /pages
Returns a paginated list of published pages/posts for SEO analysis.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
int | 1 | Page number |
per_page |
int | 100 | Items per page (max 100) |
Response:
{
"success": true,
"data": [
{
"url": "https://example.com/my-post",
"title": "My Post Title",
"meta_description": "Brief description",
"h1": "My Post Title",
"word_count": 1500,
"published_at": "2024-01-15T10:00:00Z"
}
],
"pagination": {
"current_page": 1,
"per_page": 100,
"total": 350,
"last_page": 4
}
}
GET /entities
Returns a paginated list of entities (products, categories, locations, etc.) for topical authority building.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
int | 1 | Page number |
per_page |
int | 100 | Items per page (max 100) |
type |
string | null | Filter by type (product, etc.) |
Response:
{
"success": true,
"data": [
{
"id": "product-123",
"type": "product",
"name": "My Product",
"slug": "my-product",
"description": "Product description",
"url": "https://example.com/products/my-product",
"priority": 80,
"metadata": {}
}
],
"pagination": {
"current_page": 1,
"per_page": 100,
"total": 45,
"last_page": 1
}
}
POST /content-drafts
Receives a content draft from GrowthAtlas and publishes or saves it as a draft on your site.
Idempotency: The Idempotency-Key header is sent on every request. Connectors must check whether a post with the same growthatlas_draft_id already exists (via meta/custom field) and skip re-creating it. Instead, return the existing post's URL.
Request body:
{
"growthatlas_draft_id": 123,
"growthatlas_brief_id": 456,
"title": "10 Best CRM Tools for Startups in 2025",
"slug": "best-crm-tools-startups-2025",
"excerpt": "A concise excerpt for the post.",
"body": "## Introduction\n\nMarkdown body content...",
"body_html": "<h2>Introduction</h2><p>HTML body content...</p>",
"body_format": "markdown",
"meta_title": "10 Best CRM Tools for Startups | My Site",
"meta_description": "Discover the top CRM tools for startups in 2025.",
"faq": [
{
"question": "What is a CRM?",
"answer": "A CRM is..."
}
],
"schema_json": {
"@context": "https://schema.org",
"@type": "Article",
"headline": "10 Best CRM Tools"
},
"featured_image_url": "https://images.unsplash.com/photo-123",
"featured_image_alt": "CRM dashboard illustration",
"internal_links": [
{
"anchor": "email marketing tools",
"url": "/email-marketing-guide"
}
],
"tags": ["crm", "startups", "productivity"],
"categories": ["Software Reviews"],
"language": "en",
"publish_status": "draft",
"target_keyword": "best crm for startups",
"seo_score": 87,
"source": "growthatlas"
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
growthatlas_draft_id |
int | yes | Used for idempotency. Store in post meta. |
growthatlas_brief_id |
int | no | Linked brief ID. Optional, store in meta. |
title |
string | yes | Post title |
slug |
string | no | URL slug. Auto-generate from title if empty. |
excerpt |
string | no | Short description / excerpt |
body |
string | no | Body content (markdown if body_format=markdown) |
body_html |
string | no | Pre-rendered HTML body |
body_format |
string | no | markdown or html. Default markdown. |
meta_title |
string | no | SEO title. Set in Yoast/RankMath/AIOSEO if available. |
meta_description |
string | no | SEO meta description. |
faq |
array | no | FAQ items [{question, answer}] |
schema_json |
object | no | Schema.org JSON-LD object |
featured_image_url |
string | no | URL of the featured image to download and attach |
featured_image_alt |
string | no | Alt text for the featured image |
internal_links |
array | no | Suggested internal links [{anchor, url}] |
tags |
array | no | Post tags |
categories |
array | no | Post categories |
language |
string | no | ISO language code. Default en. |
publish_status |
string | no | draft or published. Default draft. |
target_keyword |
string | no | Primary SEO keyword |
seo_score |
int | no | GrowthAtlas SEO score (0-100) |
source |
string | no | Always growthatlas |
Response (success):
{
"success": true,
"data": {
"external_id": "wp-post-4521",
"url": "https://example.com/best-crm-tools-startups-2025",
"status": "draft",
"created": true
}
}
Response (idempotent — already exists):
{
"success": true,
"data": {
"external_id": "wp-post-4521",
"url": "https://example.com/best-crm-tools-startups-2025",
"status": "draft",
"created": false
}
}
Outbound Social / Inbound to GrowthAtlas
Your site (or any custom generator — ML pipeline, CMS, cron job) can push social packages into GrowthAtlas Social Hub. GrowthAtlas then manages approval, scheduling, and Instagram publishing. This is the reverse direction of the connector endpoints above (which receive SEO drafts from GrowthAtlas).
Laravel users: use the official package client
(GrowthAtlas::social()->pushPost([...])) — see
growthatlas/laravel-connector docs/inbound-social.md.
Base URL
https://growthatlas.io/api/inbound/v1
Authentication
Authorization: Bearer ga_in_…
Optional (accepted and forwarded by connector clients; reserved for a future server-side cache):
Idempotency-Key: <unique-string>
Server idempotency is keyed on external_id per Integration.
Generate the inbound token in GrowthAtlas under Integration → Inbound Social. It is
separate from your site’s connector API key (ga_live_…).
Endpoints
| Method | Path | Content-Type |
|---|---|---|
| GET | /health |
— |
| POST | /social-posts |
application/json |
| POST | /social-posts/multipart |
multipart/form-data |
| GET | /social-posts/{id} |
— |
Rate limit: 60 requests per minute per token (HTTP 429 when exceeded).
Parameter reference
| Field | Type | Required | Description |
|---|---|---|---|
external_id |
string | yes | Your stable id (idempotent per Integration). Max 191 characters. |
format |
string | yes | feed_image | feed_video | reel | carousel | story |
caption |
string | no | Post caption. Max 2200 characters. |
hashtags |
string[] | no | Hashtag strings without #. Each max 100 characters. |
scheduled_at |
ISO-8601 | no | Scheduling hint. Honored when intake mode allows; otherwise ignored. |
social_profile_ids |
int[] | no | GrowthAtlas social profile IDs. Overrides Integration defaults. |
intake_mode |
string | no | studio_draft | autopilot_queue | publish_now. Defaults to Integration setting. |
media |
array | yes | 1–10 items. See Media. |
Social profile IDs
Find profile IDs in GrowthAtlas under Social → Accounts. Set defaults on the
Integration under Inbound Social → Default social profiles. Omit social_profile_ids
to use defaults. If neither payload nor Integration provides profiles → 422
(destinations_required).
Media
Limits
| Limit | Value |
|---|---|
| Max file size (multipart upload) | 300 MB |
| Max download size (URL mode) | 300 MB (314,572,800 bytes) |
| URL download timeout | 30 seconds |
| Max items per post | 10 |
| Allowed MIME types | image/jpeg, image/png, image/webp, image/gif, video/mp4, video/quicktime |
URL mode (JSON)
"media": [
{ "url": "https://cdn.example.com/a.mp4", "mime_type": "video/mp4" }
]
- HTTPS only; publicly reachable URLs; no
localhostor private IPs. - Redirects are not followed.
mime_typeoptional but recommended; must match detected type if provided.
Multipart
media[0], media[1], … as ordered file parts (carousel order). Same logical fields as
JSON, sent as form fields. One mode per request (no mixed URL + file).
Intake modes
| Mode | Behavior |
|---|---|
studio_draft |
Lands in Social Studio as draft with media and destinations. |
autopilot_queue |
Schedules on the Integration’s linked Social Autopilot campaign cadence (linked campaign required). |
publish_now |
Submits through project approval → publish pipeline. Never bypasses required approvals. |
Idempotency and updates
- Same
external_idunder one Integration returns the existing post (no duplicate media). This is the server’s idempotency key. - The optional
Idempotency-Keyheader may be sent by clients but is not used for server-side deduplication in v1; it is reserved for a future optional cache layer. - Replace allowed while status is
draft. - When status is not
draft, the API returns the existing post unchanged (HTTP200) — safe for idempotent retries; no recomposition or media re-ingest.
Responses
Success (200):
{
"data": {
"id": 12345,
"external_id": "campaign-42-post-7",
"status": "draft",
"format": "reel",
"scheduled_at": null,
"studio_url": "https://growthatlas.io/app/projects/1/social/studio/12345/edit"
}
}
Errors:
| HTTP | When |
|---|---|
401 |
Missing or invalid inbound token |
403 |
Inbound Social disabled on Integration |
404 |
Unknown post id for this Integration |
422 |
Validation, SSRF, unsupported media, missing destinations/campaign |
429 |
Rate limit exceeded |
Examples
curl (JSON)
curl -X POST https://growthatlas.io/api/inbound/v1/social-posts \
-H "Authorization: Bearer ga_in_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"external_id": "campaign-42-post-7",
"format": "reel",
"caption": "Three tips for better engagement",
"social_profile_ids": [123],
"media": [{"url": "https://cdn.example.com/reel.mp4", "mime_type": "video/mp4"}]
}'
curl (multipart)
curl -X POST https://growthatlas.io/api/inbound/v1/social-posts/multipart \
-H "Authorization: Bearer ga_in_your_token_here" \
-F 'external_id=campaign-42-post-7' \
-F 'format=feed_image' \
-F 'caption=Generated locally' \
-F 'social_profile_ids[]=123' \
-F 'media[0]=@/path/to/image.jpg;type=image/jpeg'
Python (requests)
import requests
response = requests.post(
"https://growthatlas.io/api/inbound/v1/social-posts",
headers={"Authorization": "Bearer ga_in_your_token_here"},
json={
"external_id": "ml-run-2026-07-23-post-1",
"format": "feed_image",
"caption": "AI-generated caption",
"social_profile_ids": [123],
"media": [{"url": "https://cdn.example.com/output.jpg", "mime_type": "image/jpeg"}],
},
timeout=60,
)
response.raise_for_status()
print(response.json()["data"]["studio_url"])
Rate Limiting
GrowthAtlas respects Retry-After headers if returned. Connectors may limit requests per minute at their discretion. Recommended defaults:
POST /content-drafts: 10 requests/minute- Import endpoints: 60 requests/minute
- Inbound Social (
POST /api/inbound/v1/social-posts): 60 requests/minute
Versioning
The current version is v1. Breaking changes will result in a new version (v2). Non-breaking additions (new optional fields) are made in-place without version bumps.
The version is included in all request paths and in the X-GrowthAtlas-Version header.
Security Checklist for Connector Implementors
- Validate
Authorization: Bearertoken using constant-time comparison - Optionally verify
X-GrowthAtlas-SignatureHMAC-SHA256 - Implement idempotency for
POST /content-draftsusinggrowthatlas_draft_id - Return proper HTTP status codes (200/201 for success, 4xx for errors)
- Never expose sensitive data in error messages
- Store API keys securely (encrypted, not in plain text)
- Implement slug conflict resolution (suffix or idempotent update)
Official Connectors
| Connector | Install | Docs |
|---|---|---|
| WordPress Plugin | WordPress.org Plugin Directory or GitHub | /connector-api#wordpress |
| Laravel Package | composer require growthatlas/laravel-connector |
/connector-api#laravel |
| Custom (any stack) | Build your own using this spec | This document |