ProductConsole

SEO for Free docs

Connect your website with its address and nothing else. SEO for Free reads every page, scores it, writes the fixes and puts the approved ones live, in Africa and worldwide.

Quick start

  1. Sign in to the console with your work email and paste your website’s address, or connect it with the API.
  2. SEO for Free starts reading it straight away. No proof of ownership is needed to begin; you can add it later.
  3. Review the score, the issues and the suggested fixes. Approve the ones you like, or switch on automatic approval for safe fixes.
  4. Install the snippet (one line) or use server-side fixes, so approved fixes go live.
  5. Connect Search Console to see the searches you appear for and your positions.

Connect a website

A website is connected by its address. There is no DNS record to add, no file to upload and no waiting: SEO for Free reads it at once and keeps every read, so the score has a history from the first day.

POST/api/engine/v1/sites
curl -X POST https://seoforfree.online/api/engine/v1/sites \
  -H "Authorization: Bearer eng_sk_live_…" \
  -d '{ "url": "https://yourdomain.com", "settings": { "country": "KE", "keywords": ["masai mara safari"] } }'
# → 201 { "id": "…", "host": "yourdomain.com", "status": "connected",
#          "ownership": { "level": "none", "via": null },
#          "crawl": { "finished": true, "pages": 42, "score": 67, … } }
FieldMeaning
urlThe website’s address. A bare domain works. Private and internal addresses are refused.
nameOptional label.
settingsOptional: country (two letters), language, keywords, competitors (domains), autopilot (approve, safe or auto), business_type, excluded_paths. Bounded by your plan.
crawlSend false to connect without reading yet.

A site too large to read in one run carries on in the background; crawl.finished says which. The same site cannot be connected twice, and your plan’s site limit still applies (409). DELETE /sites/{id} disconnects it.

Proving ownership (optional)

Ownership is a level of trust, not a gate. SEO for Free shows how sure it is that a site is yours, and everything works at every level:

ownership.levelHow SEO for Free knows
noneThe site was connected by its address. Nothing has been proven.
seenThe SEO for Free snippet has reported from the site’s own pages.
confirmedGoogle Search Console is connected for the site.
verifiedA file, a DNS record or a meta tag was found, using the token in your console.
MethodWhat to add
File (recommended)https://yourdomain.com/<token>.txt containing the token. It also turns on instant indexing.
DNS recordA TXT record on your domain (or _cabana.yourdomain.com): cabana-site-verification=<token>
Meta tag<meta name="cabana-site-verification" content="<token>"> on your home page

For a verified site, SEO for Free checks every day that the record is still there and that the site is still the one you registered. If the record goes missing you have seven days to restore it. You promise that you own, or are authorised to manage, every site you connect (see the terms).

How we read your site

SEO for Free reads your pages as CabanaBot: Mozilla/5.0 (compatible; CabanaBot/1.0; +https://seoforfree.online/bot). It follows your robots.txt (rules for CabanaBot, then *), reads your sitemaps, and never fills in forms or signs in. See CabanaBot for details.

The snippet

One line before </head> on every page. The key is publishable and only works on pages of a site you have connected.

<script async src="https://seoforfree.online/e.js" data-key="eng_pk_live_…"></script>

It applies the fixes you approved for each page (title, description, canonical, preview cards, structured data, image alt text, language, viewport), then sends one anonymous page view: where the visitor came from, how fast the page was for them, and whether the page content changed. No cookies, no visitor ID and no IP address are kept. Do Not Track and Global Privacy Control switch the report off.

Google runs JavaScript and sees fixes the snippet applies. Some other crawlers and link previews (WhatsApp, for example) do not, so for those use server-side fixes too.

Where to paste it

  • WordPress: a header and footer code plugin → Header.
  • Shopify: Online Store → Themes → Edit code → theme.liquid, before </head>.
  • Wix: Settings → Custom code → Head, all pages.
  • Squarespace: Settings → Advanced → Code injection → Header.
  • Webflow: Project settings → Custom code → Head code.

Server-side fixes

For sites that render on the server (Next.js, Nuxt, Laravel, Django, Rails…), fetch the ready-made tags for a page and print them in its <head>. Cache them for a few minutes.

GET/api/engine/v1/fixes?url=https://yourdomain.com/safaris
curl "https://seoforfree.online/api/engine/v1/fixes?url=https://yourdomain.com/safaris" \
  -H "Authorization: Bearer eng_sk_live_…"
# → { "url": "…", "fixes": [ { "kind": "title", "value": { "text": "…" } }, … ], "head_html": "<title>…" }

Approving fixes

SEO for Free writes each fix from your own page: rewritten titles and descriptions never add a price, number or claim that is not already on the page. You choose: approve each one, or let SEO for Free approve the safe kinds automatically (canonical, viewport, preview cards and structured data built from your content). A fix goes live within minutes of approval; undo it at any time.

Instant indexing

When a page changes or a fix goes live, SEO for Free tells Bing, Yandex, Naver, Seznam and other IndexNow search engines at once, using your verification file as the key. Google finds changes through your sitemap and, if connected, Search Console.

Google Search Console

In the console, open your site → Rankings. Copy the service account address shown there and, in Search Console → Settings → Users and permissions, add it as a Restricted user. SEO for Free then reads your searches, clicks, impressions and positions every day. It cannot change anything in Search Console, and you can remove it at any time.

Competitors

SEO for Free finds the sites that actually rank for your keywords in your country and reads their pages the same way it reads yours, so you see their score next to yours, check by check. You can also name competitors yourself. SEO for Free only reads their public pages, as CabanaBot, following their robots.txt.

Speed and traffic

With the snippet installed, each visit reports Core Web Vitals (LCP, CLS, INP), device type and where it came from: search engines, AI assistants, social, email or direct. Without it, SEO for Free uses Google’s public Chrome UX Report when your site has enough traffic.

The SEO Score

Each page is checked against 83 checks in nine areas (technical, on-page, content, structured data, speed, social previews, AI search, local & Africa, and trust & security) and scored from 0 to 100; the site score weights the pages that matter most. Bands: Excellent 90+, Strong 75–89, Fair 55–74, Weak 35–54, Critical below 35.

Measured, reported, estimated

Every figure SEO for Free produces says how it is known, in the console, on the public reading and in the API (basis):

basisMeaning
measuredSEO for Free saw it itself, just now: a header, a tag, a redirect, a robots.txt line.
reportedAn outside source says so and is named in source: a domain registry, the Internet Archive, Tranco, Chrome’s field data, RIPE NCC, public DNS.
estimate / inferredWorked out from other facts, such as the speed of a page from its size, or the technology behind a site from its headers. Counted at reduced weight in the score and always labelled.

When SEO for Free cannot know something it says so, and never fills the gap: an unreachable site is state: "unreachable" with the reason, a firewall that refuses SEO for Free is state: "blocked" with the firewall named, and a page robots.txt closes to CabanaBot is state: "robots_blocked". SEO for Free does not disguise itself to get past a firewall.

Authentication

Base URL https://seoforfree.online/api/engine/v1. Send a secret key in Authorization: Bearer eng_sk_… (or X-Api-Key) from your server only.

Endpoints

POST/sites

Connect a website by its address. See Connect a website.

GET/sites

Your websites with their status (connected or verified), ownership, score, band and last read.

GET/sites/{id}

One site: score, scores by area and a summary of the top issues.

GET/sites/{id}/pages?limit=200

Every page read, worst first, with its score and issues.

POST/sites/{id}/crawl

Read the site again now. Counts towards your plan’s reads.

DELETE/sites/{id}

Disconnect the site and delete what SEO for Free holds for it.

GET/fixes?url=…

Approved fixes and ready-made head tags for one page. See server-side fixes.

GET/check?url=…

A free reading of any public website, with no key. See The public reading.

The public reading

The box on our home page is an API you can use. GET /api/engine/v1/check?url=example.co.ke needs no key and returns a reading: the home page and up to seven more chosen from the menu and the sitemap, audited with all 83 checks, plus robots.txt, the AI crawler matrix, sitemaps, the four address variants, the certificate, DNS and domain registration. It obeys robots.txt first. Limits: 8 fresh readings per ten minutes per network; a repeat of a reading we hold is free for 30 minutes. Nothing is stored in a database.

# the shape of a reading
{ "state": "ok", "score": 67, "band": { "label": "Fair" }, "scores": { "technical": 91, … }, "basis": { "speed": "estimate", … },
  "confidence": { "level": "high", "reasons": [ "8 pages were read of about 113 the site lists, chosen across its sections." ] },
  "issues": [ { "id": "…", "title": "…", "problem": "…", "how": "…", "pages": 3, "fix": "title" } ],
  "pages": [ … ], "home": { "facts": { … }, "headers": { … }, "tech": [ … ] }, "site": { "robots": { "ai": { "agents": [ … ] } }, "sitemap": { … } },
  "intel": { "dns": { "ok": true, "basis": "reported", "source": "…", "data": { … } }, … },
  "modules": { "pending": [ "history", "rank", "hosting", "vitals", "lab" ], "token": "…" } }

The slower questions are asked separately, so a page can fill in as answers arrive. Each takes the token the reading gave, which ties the question to a reading that really happened.

ModuleSource
GET /scan/history?host=…&token=…The Internet Archive: first and last snapshot.
/scan/rankTranco: popularity among the world’s top million sites, with a month of history.
/scan/hostingRIPE NCC: network, place, and the fastest a reply could reach African cities (only when no CDN fronts the site).
/scan/vitalsChrome UX Report: real visitors’ Core Web Vitals on phones. Needs a Google API key on the server; answers no_key without one.
/scan/labGoogle PageSpeed Insights: a Lighthouse run. 20 to 40 seconds.
/scan/compare?rivals=a.com,b.comReads up to three rivals the same way and compares only on the checks both sites could be judged on. Limited to 5 a ten minutes.
/scan/competitorsWho appears in search results for the phrases the site should be found for. Needs a search data source on the server and says so when it has none.

Each module answers { id, ok, basis, source, at, data }, or { ok: false, error } with no_key, timeout and similar codes. Every one is its own request: a slow source never delays the others.

Webhooks

EventWhen
engine.crawl.completedA read of your site finished: new score, previous score, pages and top issues
property.verifiedA website was verified
property.updatedIts standing changed in the daily look
pingA test from the console

Each POST carries Cabana-Signature: t=<unix seconds>,v1=<hex>: an HMAC-SHA256 of t + "." + raw body under the endpoint’s signing secret. Reject anything older than five minutes. Failed deliveries are retried after 1, 5 and 30 minutes, then 2, 6 and 24 hours.

import { createHmac, timingSafeEqual } from 'node:crypto';
export function verified(rawBody, header, secret) {
  const p = Object.fromEntries(String(header).split(',').map(x => x.split('=')));
  if (!p.t || Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
  const want = createHmac('sha256', secret).update(`${p.t}.${rawBody}`).digest('hex');
  return want.length === String(p.v1).length && timingSafeEqual(Buffer.from(want), Buffer.from(p.v1));
}

Errors and limits

Errors are JSON: { "error": "…", "code": "…" } with 400 (bad request), 401 (invalid_key), 402 (trial_ended), 403 (wrong_key_kind, origin_not_verified), 404, 409 (not_connected; a site already connected; the plan’s site limit), 429 (quota_exceeded). The free trial covers one site, 100 pages per read, 30 reads, 3 competitors and 1,000 API calls a month.