Astrina Developer API
One REST JSON API for your traffic stats, reviews, ratings, phone intelligence, 32 runnable utilities and social login. Simple key auth, generous free tier.
Authentication & quota
Every request needs your API key. Get one on your API dashboard. Pass it in a request header — keys are never accepted in the URL, so they cannot leak into server logs, proxies or Referer headers:
curl https://astrina.io/api/v1/rating?site=example.com \
-H "X-Api-Key: ak_your_key_here"
# or: Authorization: Bearer ak_your_key_hereYour daily quota comes with your plan: Free 100/day, Pro 5,000/day, Business 25,000/day, Business Plus 100,000/day — upgrade your plan for a higher limit. Every response echoes your quota. Errors return an "error" field with the right status: 401 (bad key), 429 (quota), 400/422 (bad params), 403/404 (not yours / not found). Responses are CORS-open and noindex.
One REST API to publish, schedule, measure and message across every network you connect — 18 social networks and 6 ad networks, one auth model, one request shape.
Supported networks (18): X (Twitter), Instagram, Facebook, Threads, LinkedIn, TikTok, YouTube, Pinterest, Reddit, Snapchat, Google Business, VK, Odnoklassniki, Telegram, WhatsApp, Discord, Mastodon, Bluesky. Ad networks: Meta Ads, Google Ads, TikTok Ads, LinkedIn Ads, Pinterest Ads, X Ads.
Same key auth as everything above (header only), scoped to social. Connect channels on your Social dashboard. Publishing is asynchronous: /publish returns the post with each target queued; the worker delivers within the minute and a webhook fires when it lands.
/api/v1/social/channels Every network, its capabilities, and which you have connected. Call this first./api/v1/social/accounts The channels you have connected — the ids /publish targets./api/v1/social/publish Publish or schedule one post to one or many channels.| Param | Description |
|---|---|
text | Post body (required unless media given) |
channels | "all", or a list of channel ids from /accounts |
media | Array of {url,type,alt} (optional) |
scheduled_at | ISO-8601 time; omit or past = publish now |
idempotency_key | Your id for the post — a retry with the same key returns the first, never double-posts |
overrides | Per-channel body overrides, keyed by channel id |
POST /api/v1/social/publish
Authorization: Bearer <your API key>
Content-Type: application/json
{ "text": "We just shipped 🚀", "channels": "all",
"idempotency_key": "launch-42" }
{ "ok": true, "post": {
"id": 1841, "status": "publishing", "text": "We just shipped 🚀",
"targets": [
{ "channel_id": 12, "provider": "telegram", "status": "queued", "url": null },
{ "channel_id": 31, "provider": "mastodon", "status": "queued", "url": null } ] },
"quota": { "used": 3, "limit": 25000 } }/api/v1/social/post One post and every target's delivery outcome (?id=…), or the recent list./api/v1/social/analytics Followers, impressions, reach and engagement — a channel series or an account roll-up. Snapshots, never estimates./api/v1/social/messages Read a DM thread, or send a direct message on a channel that supports it./api/v1/social/comments Comments captured on your published posts./api/v1/social/ads List and create ad campaigns across the six ad networks./api/v1/social/webhooks Get told when a post lands or a comment/DM/mention arrives.Events: post.published, post.partial, post.failed, comment.received, dm.received, mention.received. A generic hook is signed with X-Astrina-Signature: sha256=hmac(timestamp.body, secret).
Analytics
/api/v1/stats Traffic analytics for a site you own.| Param | Description |
|---|---|
site | Site id or domain you own (required) |
period | 24h · 7d (default) · 30d · 90d |
report | summary (default) · timeseries · pages · sources · countries · devices |
limit | Rows for breakdown reports (1–100, default 20) |
GET /api/v1/stats?site=example.com&period=30d
{
"site": "example.com", "site_id": 4, "period": "30d",
"summary": {
"visitors": 18204, "pageviews": 54881, "visits": 24310,
"pages_per_visit": 2.26, "online_now": 7
},
"top_sources": [{ "source": "google.com", "visitors": 9021 }, …],
"top_countries": [{ "country": "US", "visitors": 7440 }, …],
"quota": { "used": 12, "limit": 100 }
}Conversions
/api/v1/conversion Record a conversion from your server, so revenue that never touches the browser is still attributed.| Param | Description |
|---|---|
site | Site id or domain you own (required) |
kind | lead · qualified · deal · sale · recurring · refund · churn |
value | Amount, in the currency below. Negative for a refund. |
currency | Three-letter code, default USD |
uid | Visitor id from the tracking script. Optional — see the note below. |
external_id | Your own id for the record, used to avoid double-counting a retry |
POST /api/v1/conversion
Authorization: Bearer <your API key>
site=example.com&kind=sale&value=249.00¤cy=USD&uid=v_8f3c…&external_id=order-1042
{ "recorded": true, "id": 5512, "kind": "sale" }A conversion without a uid is recorded, not rejected: a sale closed over the phone is still revenue. It counts in totals and is reported as unattributed rather than being assigned to a channel it cannot be traced to.
Minify
/api/v1/minify Minify HTML, CSS or JS and get the compressed bytes back. Runs on our servers (no AI tokens); billed per request by input size.| Param | Description |
|---|---|
kind | html · css · js — required (via ?kind=, the uploaded filename, or Content-Type) |
| body | The source to minify: the raw request body, a multipart file, or JSON {"kind":"…","data":"…"} |
POST /api/v1/minify?kind=css
X-Api-Key: ak_your_key_here
.button{ color: #ffffff; margin: 0 0 0 0; }
{ "kind":"css", "in_bytes":42, "out_bytes":24, "saved_pct":43,
"hash":"be6ca66f…", "output":".button{color:#fff;margin:0}" }Auth: header only (X-Api-Key or Bearer), scope minify. CSS and JS are minified safely — calc() spacing and JS ASI are preserved; HTML collapses whitespace while keeping <pre>, <textarea>, <script>, <style> and JSON-LD intact. Track usage and keys on your Minify dashboard.
Originality & AI detection
/api/v1/originality Start one check: text uniqueness, AI-written text, image uniqueness or AI-generated image. Returns a run id immediately — the work happens on a worker.| Param | Description |
|---|---|
kind | text-unique · ai-text · image-unique · ai-image — required |
text | the text to check (text modules) |
url | read the text — or fetch the image — from this address instead |
image | multipart file field (image modules); or image_base64 |
POST /api/v1/originality
X-Api-Key: ak_your_key_here
kind=ai-text&text=In+today%27s+rapidly+evolving+digital+landscape…
{ "ok":true, "run":1042, "kind":"ai-text", "status":"queued",
"poll":"https://astrina.io/api/v1/originality?run=1042",
"units_charged":5, "balance_charged_usd":0,
"checks_used_today":7, "checks_per_day":500 }Auth: header only (X-Api-Key or Bearer), scope originality. Pro and above. One task costs a fixed number of API requests from your daily quota — 10 for text uniqueness, 5 for AI-text, 5 for image uniqueness, 3 for AI-image — because one task is a dozen index queries, a dozen page downloads or a few hundred model evaluations. Beyond your plan’s daily allowance of checks, a task also costs a fixed price from your balance, and the response says exactly what was charged.
/api/v1/originality?run=<id> Poll a run. While it works you get the stage it is in; when it finishes you get the full report.GET /api/v1/originality?run=1042
X-Api-Key: ak_your_key_here
{ "ok":true, "run":1042, "kind":"ai-text", "status":"running",
"progress":75, "stage":"model", "stage_label":"Scoring every sentence against a language model" }
{ "ok":true, "status":"done", "score":88.4, "verdict":"ai", "confidence":71,
"report":{ "per_sentence":[…], "features":{…}, "notes":[…] } }Polling costs 1 request. A text-uniqueness pass typically finishes in 15–40 seconds; poll every 2–3 seconds. Every report carries its confidence and the evidence behind it: matched pages with quoted fragments, or the measurements each signal produced. Detection is probabilistic — the API never returns a verdict without the numbers it came from.
/api/v1/originality The de-AI layer, same door: kind=deai-text rewrites a draft so it stops reading as machine-written; kind=deai-image re-codes a generated picture so its pixels behave like a photograph.POST /api/v1/originality
X-Api-Key: ak_your_key_here
kind=deai-text&text=In+today%27s+rapidly+evolving+digital+landscape…
{ "ok":true, "run":1043, "kind":"deai-text", "status":"queued", "units_charged":15 }
GET /api/v1/originality?run=1043
{ "status":"done", "score":41.2,
"report":{ "before":93.0, "after":41.2, "target":70, "reached_target":true,
"changes":["4 stock transition(s) removed","1 sentence pair(s) joined for rhythm"],
"paid_pass":true, "text":"…the rewritten text…" } }15 API requests for a text rewrite, 6 for an image re-code — a rewrite can spend a model call, and the price covers it. Each task gets ONE free deterministic pass and at most ONE paid model rewrite: if that is not enough, the answer says so instead of spending again. An image task answers with a download link (kept for 72 hours). The rewriter makes edits an editor would defend — stock transitions cut, rhythm varied, over-used words replaced — and re-measures with the same detector. It does not add mistakes and it does not invent facts. The image re-coder refuses files carrying a signed C2PA manifest rather than stripping them, and writes an honest “AI-assisted illustration” credit into what it produces.
/api/v1/originality The catalogue: what each check costs in API requests and in money, and how much of your allowance is left today.Reviews & ratings
/api/v1/reviews Latest reviews + aggregate rating for any subject.| Param | Description |
|---|---|
type | site · phone · email · address · org · shop |
key | The subject value (domain, number, email, …) |
GET /api/v1/reviews?type=site&key=example.com
{ "type": "site", "subject": "example.com", "rating": 4.3, "count": 87,
"reviews": [{ "author": "Jane", "rating": 5, "body": "…", "date": "2026-07-01" }, …],
"url": "https://astrina.io/reviews/site/example.com/" }/api/v1/rating Compact rating summary + star distribution + embed URLs.| Param | Description |
|---|---|
site | Shortcut for a website by domain |
type + key | Or any subject by type + key |
GET /api/v1/rating?site=example.com
{ "type": "site", "subject": "example.com", "rating": 4.3, "count": 87,
"distribution": { "1": 3, "2": 4, "3": 9, "4": 26, "5": 45 },
"reviews_url": "https://astrina.io/reviews/site/example.com/",
"widget_url": "https://astrina.io/widget.php?type=site&key=example.com",
"badge_url": "https://astrina.io/badge.php?s=4" }Phone intelligence
/api/v1/phone Carrier, line type and community spam-risk for a number.| Param | Description |
|---|---|
number | Phone number in international format, e.g. +14155550123 |
GET /api/v1/phone?number=+14155550123
{ "number": "+14155550123", "country": "United States", "iso": "US",
"operator": "…", "line_type": "mobile",
"risk": { "score": 12, "verdict": "low", "confidence": 0.6, "reports": 3, … },
"url": "https://astrina.io/reviews/phone/+14155550123/" }Blacklist & spam check
/api/v1/blacklist Check if a phone, email, domain or site is on the spam / scam blacklist.| Param | Description |
|---|---|
value | The phone, email, domain or URL to check (type auto-detected). |
type | Optional — force one of phone · email · domain |
GET /api/v1/[email protected] { "value": "[email protected]", "listed": true, "categories": ["disposable"], "matches": [ { "type": "email_domain", "match": "mailinator.com", "category": "disposable", "source": "disposable-email-domains", "confidence": 90, "added": "2026-07-12 15:46:00" } ], "quota": { "used": 7, "limit": 100000 } }
Crypto AML check
/api/v1/aml Screen a crypto wallet address against sanctions and threat-intelligence lists — chain auto-detected, risk score included.| Param | Description |
|---|---|
address | The wallet address to screen. BTC, EVM (0x…), TRON, Solana, TON, LTC, DOGE, XRP, XMR and BCH shapes are recognized; any other string is looked up as-is. |
format | Optional — set to csv to receive CSV instead of JSON. |
GET /api/v1/aml?address=TPPR7e8hGC57dexrE2jy1f94wtuGyVP6Dp
{ "address": "TPPR7e8hGC57dexrE2jy1f94wtuGyVP6Dp",
"chain": "tron", "listed": true, "risk": 100, "level": "sanctioned",
"categories": ["sanctioned"],
"matches": [
{ "category": "sanctioned", "source": "US OFAC SDN sanctions list",
"jurisdiction": "US", "risk": 100, "confidence": 88, "added": "2026-08-10" } ],
"quota": { "used": 1, "limit": 100 } }Bulk: POST an "addresses" array (up to 50) to screen many in one call — one quota unit each.
POST /api/v1/aml
{ "addresses": ["0x…", "bc1…", "T…"] }
{ "results": [ { "address": "0x…", "listed": false, "risk": 0, … } ], "count": 3 }Every response carries X-RateLimit-Limit and X-RateLimit-Remaining headers. Full documentation, risk model and live boards: astrina.io/aml/
Password leak check
/api/v1/pwned Check whether a password has appeared in a data breach — using k-anonymity, so the password never leaves the client.Hash the password with SHA-1 in your app, then send only the first 5 hex characters of that hash. The API returns every breached hash suffix that shares the prefix; you compare locally. The plain password is never transmitted.
| Param | Description |
|---|---|
prefix | First 5 hex chars of the SHA-1 hash (recommended — k-anonymity). Returns matching suffixes with breach counts. |
hash | Alternative: the full 40-hex SHA-1. Convenient, but the hash leaves the client — prefer prefix where possible. |
GET /api/v1/pwned?prefix=5BAA6
{ "mode": "range", "prefix": "5BAA6", "returned": 1978,
"suffixes": [
{ "suffix": "1E4C9B93F3F0682250B6CF8331B7EE68FD8", "count": 52372427 }, ... ],
"quota": { "used": 22, "limit": 100000 } }
# convenience mode — send the whole SHA-1, get a direct verdict:
GET /api/v1/pwned?hash=5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
{ "mode": "hash", "found": true, "count": 52372427,
"note": "This password appears in known breaches - do not use it.",
"quota": { "used": 23, "limit": 100000 } }Data source: Have I Been Pwned (Pwned Passwords). The same k-anonymity model powers the free browser tool at /password-check/.
Utilities
Run our synchronous server-side utilities over the API — 31 of the 141. Browser-only utilities and queued crawls/renders are not exposed. Every utility takes the same shape and returns the same envelope; the plan gates and daily run caps are identical to the website.
GET /api/v1/tool # the catalogue GET /api/v1/tool?name=<key>&value=<input> # run one (value2 for two-input utilities) -H "X-Api-Key: ak_your_key_here"
| Param | Description |
|---|---|
name | The utility key (see each block below). |
value | The main input — a URL, domain, comma/newline-separated URLs, or text. |
value2 | The second input, only for utilities that compare two things. |
Errors: 404 unknown utility · 400 not API-runnable · 403 plan required · 429 daily run cap reached · 422 bad input. Each returns an "error" field.
🔍 SEO (18)
/api/v1/tool?name=site-audit 🔍 Technical, speed and security in one pass.GET /api/v1/tool?name=site-audit&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "site-audit",
"ms": 807,
"result": {
"summary": [
{
"label": "Overall",
"value": "93/100",
"state": "ok"
},
{
"label": "SEO",
"value": "90/100",
"state": "ok"
},
{
"label": "Performance",
"value": "90/100",
"state": "ok"
},
"…"
],
"cols": [
"Area",
"What failed",
"What to do"
],
"rows": [
{
"a": "SEO",
"b": "Headings in logical order",
"c": "Nest headings sequentially (H1→H2→H3) without skipping levels.",
"state": "bad"
},
{
"a": "SEO",
"b": "All images have alt",
"c": "Add descriptive alt to every <img> (alt=\"\" only for decorative).",
"state": "bad"
},
{
"a": "SEO",
"b": "Form inputs have labels",
"c": "Pair every input with a <label for> or an aria-label.",
"state": "warn"
},
"…"
],
"notes": []
}
}/api/v1/tool?name=serp-preview 👁️ How the snippet looks in search results.GET /api/v1/tool?name=serp-preview&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "serp-preview",
"ms": 127,
"result": {
"summary": [
{
"label": "Title length",
"value": "53 chars",
"state": "ok"
},
{
"label": "Description length",
"value": "163 chars",
"state": "warn"
},
{
"label": "Title truncated",
"value": "no",
"state": "ok"
},
"…"
],
"cols": [
"Part",
"Value"
],
"rows": [
{
"a": "Headline shown",
"b": "Every site you look after, in one dashboard — Astrina",
"state": ""
},
{
"a": "Breadcrumb shown",
"b": "astrina.io",
"state": ""
},
{
"a": "Snippet shown",
"b": "Real traffic, daily SEO checks and reviews for your sites and your clients’ sites. One …",
"state": ""
},
"…"
],
"notes": [
"Google cuts on pixel width, not character count, and rewrites titles on roughly a third…"
]
}
}/api/v1/tool?name=og-preview 📣 The card shown when your link is shared.GET /api/v1/tool?name=og-preview&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "og-preview",
"ms": 499,
"result": {
"summary": [
{
"label": "og:title",
"value": "set",
"state": "ok"
},
{
"label": "og:description",
"value": "set",
"state": "ok"
},
{
"label": "og:image",
"value": "set",
"state": "ok"
},
"…"
],
"cols": [
"Field",
"Value"
],
"rows": [
{
"a": "Card title",
"b": "Every site you look after, in one dashboard — Astrina",
"state": ""
},
{
"a": "Card description",
"b": "Real traffic, daily SEO checks and reviews for your sites and your clients’ sites. One …",
"state": ""
},
{
"a": "Card image",
"b": "https://astrina.io/og.php",
"state": "ok"
},
"…"
],
"notes": [
"Facebook, X and LinkedIn each cache what they scraped. After fixing tags, re-scrape in …"
]
}
}/api/v1/tool?name=schema-validator 🧬 Validates JSON-LD structured data.GET /api/v1/tool?name=schema-validator&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "schema-validator",
"ms": 75,
"result": {
"summary": [
{
"label": "JSON-LD blocks",
"value": "2",
"state": "ok"
},
{
"label": "Types found",
"value": "2",
"state": "ok"
},
{
"label": "Invalid blocks",
"value": "0",
"state": "ok"
},
"…"
],
"cols": [
"Where",
"Type",
"Verdict"
],
"rows": [
{
"a": "block 1",
"b": "Organization",
"c": "required properties present",
"state": "ok"
},
{
"a": "block 2",
"b": "WebSite",
"c": "required properties present",
"state": "ok"
}
],
"notes": [
"Valid markup is not the same as an eligible rich result. Google also applies content po…"
]
}
}/api/v1/tool?name=meta-tags 🏷️ Title, description and og tags with lengths.GET /api/v1/tool?name=meta-tags&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "meta-tags",
"ms": 164,
"result": {
"summary": [
{
"label": "Title length",
"value": "53",
"state": "ok"
},
{
"label": "Description length",
"value": "163",
"state": "warn"
},
{
"label": "H1 count",
"value": "1",
"state": "ok"
},
"…"
],
"cols": [
"Tag",
"Value",
"Length",
"…"
],
"rows": [
{
"a": "title",
"b": "Every site you look after, in one dashboard — Astrina",
"c": "53",
"d": "Good length.",
"state": "ok"
},
{
"a": "meta description",
"b": "Real traffic, daily SEO checks and reviews for your sites and your clients’ sites. One …",
"c": "163",
"d": "Long — the snippet is cut around 160 characters.",
"state": "warn"
},
{
"a": "canonical",
"b": "https://astrina.io/",
"c": "19",
"d": "Declared.",
"state": "ok"
},
"…"
],
"notes": []
}
}/api/v1/tool?name=keyword-density 📊 Density and frequency of words on the page.GET /api/v1/tool?name=keyword-density&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "keyword-density",
"ms": 93,
"result": {
"summary": [
{
"label": "Words",
"value": "1093",
"state": "ok"
},
{
"label": "Unique words",
"value": "503",
"state": ""
},
{
"label": "Vocabulary richness",
"value": "46.0%",
"state": "ok"
},
"…"
],
"cols": [
"Kind",
"Term",
"Count",
"…"
],
"rows": [
{
"a": "single word",
"b": "one",
"c": "20",
"d": "1.83%",
"e": "title, H1, description",
"state": "ok"
},
{
"a": "single word",
"b": "reviews",
"c": "17",
"d": "1.56%",
"e": "description",
"state": "ok"
},
{
"a": "single word",
"b": "listed",
"c": "15",
"d": "1.37%",
"e": "body only",
"state": ""
},
"…"
],
"notes": [
"Density is a diagnostic, not a target. No search engine has a threshold to hit — this i…"
]
}
}/api/v1/tool?name=redirects ↪️ Follows 301/302 chains hop by hop.GET /api/v1/tool?name=redirects&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "redirects",
"ms": 1753,
"result": {
"summary": [
{
"label": "Final status",
"value": "200",
"state": "ok"
},
{
"label": "Redirects followed",
"value": "0",
"state": "ok"
},
{
"label": "Ends at",
"value": "https://astrina.io/",
"state": ""
},
"…"
],
"cols": [
"Address",
"Hop",
"Status",
"…"
],
"rows": [
{
"a": "as entered",
"b": "1",
"c": "200",
"d": "https://astrina.io/",
"e": "— final destination —",
"state": "ok"
},
{
"a": "http://astrina.io/",
"b": "1",
"c": "200",
"d": "http://astrina.io/",
"e": "— final destination —",
"state": "ok"
},
{
"a": "https://astrina.io/",
"b": "1",
"c": "200",
"d": "https://astrina.io/",
"e": "— final destination —",
"state": "ok"
},
"…"
],
"notes": [
"The http, https, www and apex forms of this address do not all end at the same URL. Sea…",
"The page contains JavaScript that changes location. A crawler may or may not follow it,…",
"Every hop above was requested and its answer read; nothing in the chain is guessed from…"
]
}
}/api/v1/tool?name=sitemap 🗺️ Validates the XML sitemap and its index.GET /api/v1/tool?name=sitemap&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "sitemap",
"ms": 2646,
"result": {
"summary": [
{
"label": "Type",
"value": "sitemap index",
"state": "ok"
},
{
"label": "URLs found",
"value": "5408",
"state": "ok"
},
{
"label": "Unique URLs",
"value": "5405",
"state": "warn"
},
"…"
],
"cols": [
"Child sitemap",
"Status",
"URLs",
"…"
],
"rows": [
{
"a": "https://astrina.io/sitemap-core.xml",
"b": "200",
"c": "534",
"d": "59.9 KB",
"state": "ok"
},
{
"a": "https://astrina.io/sitemap-blog.xml",
"b": "200",
"c": "42",
"d": "5.2 KB",
"state": "ok"
},
{
"a": "https://astrina.io/sitemap-catalog.xml",
"b": "200",
"c": "61",
"d": "6.9 KB",
"state": "ok"
},
"…"
],
"notes": [
"Started from the sitemap declared in robots.txt: https://astrina.io/sitemap.xml",
"3 URLs appear more than once.",
"7 addresses were fetched, within your plan cap of 25."
]
}
}/api/v1/tool?name=html-validate ✅ Structural errors against W3C rules.GET /api/v1/tool?name=html-validate&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "html-validate",
"ms": 180,
"result": {
"summary": [
{
"label": "Errors",
"value": "8",
"state": "bad"
},
{
"label": "Warnings",
"value": "0",
"state": "ok"
},
{
"label": "Total findings",
"value": "8",
"state": "warn"
},
"…"
],
"cols": [
"Line",
"Level",
"Finding"
],
"rows": [
{
"a": "314",
"b": "error",
"c": "htmlParseEntityRef: expecting ';'",
"state": "bad"
},
{
"a": "513",
"b": "error",
"c": "Unexpected end tag : svg",
"state": "bad"
},
{
"a": "514",
"b": "error",
"c": "Unexpected end tag : svg",
"state": "bad"
},
"…"
],
"notes": [
"420 \"unknown element\" complaints were discarded. The parser behind this check knows the…",
"This is a structural check — parse errors, duplicate ids, obsolete and required element…"
]
}
}/api/v1/tool?name=robots-txt 🤖 Parses the file and resolves it per crawler.GET /api/v1/tool?name=robots-txt&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "robots-txt",
"ms": 133,
"result": {
"summary": [
{
"label": "robots.txt",
"value": "found",
"state": "ok"
},
{
"label": "Size",
"value": "1.9 KB",
"state": "ok"
},
{
"label": "Groups",
"value": "10",
"state": ""
},
"…"
],
"cols": [
"Crawler",
"This URL",
"Deciding rule",
"…"
],
"rows": [
{
"a": "*",
"b": "allowed",
"c": "Allow: /",
"d": "the * group",
"state": "ok"
},
{
"a": "Googlebot",
"b": "allowed",
"c": "Allow: /",
"d": "the * group",
"state": "ok"
},
{
"a": "Bingbot",
"b": "allowed",
"c": "Allow: /",
"d": "the * group",
"state": "ok"
},
"…"
],
"text": "# As a condition of accessing this website, you agree to abide by the following\n# conte…",
"notes": [
"At least one group carries Disallow: / — that blocks an entire crawler from the whole s…"
]
}
}/api/v1/tool?name=images-seo 🖼️ Alt text, dimensions and formats.GET /api/v1/tool?name=images-seo&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "images-seo",
"ms": 1910,
"result": {
"summary": [
{
"label": "Images",
"value": "7",
"state": "ok"
},
{
"label": "Missing alt",
"value": "0",
"state": "ok"
},
{
"label": "Decorative (alt=\"\")",
"value": "7",
"state": ""
},
"…"
],
"cols": [
"Image",
"Alt text",
"Size",
"…"
],
"rows": [
{
"a": "https://astrina.io/shot.php?s=1",
"b": "(empty)",
"c": "46.6 KB",
"d": "empty alt (decorative); no width/height — causes layout shift",
"state": "warn"
},
{
"a": "https://astrina.io/shot.php?s=4",
"b": "(empty)",
"c": "75.1 KB",
"d": "empty alt (decorative); no width/height — causes layout shift",
"state": "warn"
},
{
"a": "https://astrina.io/shot.php?s=8",
"b": "(empty)",
"c": "68.1 KB",
"d": "empty alt (decorative); no width/height — causes layout shift",
"state": "warn"
},
"…"
],
"notes": [
"6 images declare no width and height. The browser cannot reserve space, so the page jum…",
"None of the sampled images are WebP or AVIF. Converting usually removes a third of the …",
"Optimizing the sampled images could save roughly 131 KB. Compress or convert them to We…",
"…"
]
}
}/api/v1/tool?name=cms-detect 🧱 Identifies the CMS and technology stack.GET /api/v1/tool?name=cms-detect&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "cms-detect",
"ms": 135,
"result": {
"summary": [
{
"label": "CMS",
"value": "Magento",
"state": "ok"
},
{
"label": "Signals found",
"value": "2",
"state": "ok"
},
{
"label": "Server",
"value": "cloudflare",
"state": ""
},
"…"
],
"cols": [
"Kind",
"Name",
"Evidence",
"…"
],
"rows": [
{
"a": "CMS",
"b": "Magento",
"c": "markup: mage/",
"d": "strong",
"state": "ok"
},
{
"a": "CDN / Edge",
"b": "Cloudflare",
"c": "server: cloudflare",
"d": "strong",
"state": "ok"
}
],
"notes": [
"This reads fingerprints, not an inventory. A strong match is reliable; a weak one is a …"
]
}
}/api/v1/tool?name=readability 📖 Text difficulty and readability metrics.GET /api/v1/tool?name=readability&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "readability",
"ms": 94,
"result": {
"summary": [
{
"label": "Reading ease",
"value": "47.0 — difficult",
"state": "warn"
},
{
"label": "Grade level",
"value": "12.6",
"state": "warn"
},
{
"label": "Gunning Fog",
"value": "15.5",
"state": "warn"
},
"…"
],
"cols": [
"Measure",
"Value"
],
"rows": [
{
"a": "Formula used",
"b": "Flesch Reading Ease and Flesch–Kincaid grade",
"state": ""
},
{
"a": "Language detected",
"b": "Latin script",
"state": ""
},
{
"a": "Syllables per word",
"b": "1.60",
"state": ""
},
"…"
],
"notes": [
"10 sentences run past 25 words.",
"These formulas count syllables and sentence lengths. They cannot tell whether the writi…"
]
}
}/api/v1/tool?name=hreflang 🌍 International markup, including return links.GET /api/v1/tool?name=hreflang&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "hreflang",
"ms": 907,
"result": {
"summary": [
{
"label": "hreflang tags",
"value": "4",
"state": "ok"
},
{
"label": "Problems",
"value": "0",
"state": "ok"
},
{
"label": "x-default",
"value": "yes",
"state": "ok"
},
"…"
],
"cols": [
"Code",
"URL",
"Links back",
"…"
],
"rows": [
{
"a": "en",
"b": "https://astrina.io/",
"c": "n/a",
"d": "ok",
"state": "ok"
},
{
"a": "ru",
"b": "https://astrina.io/ru/",
"c": "yes",
"d": "ok",
"state": "ok"
},
{
"a": "uk",
"b": "https://astrina.io/uk/",
"c": "yes",
"d": "ok",
"state": "ok"
},
"…"
],
"notes": [
"Return links were checked on 2 alternates."
]
}
}/api/v1/tool?name=links 🔀 Internal, external and nofollow links.GET /api/v1/tool?name=links&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "links",
"ms": 104,
"result": {
"summary": [
{
"label": "Internal links",
"value": "116",
"state": "ok"
},
{
"label": "External links",
"value": "0",
"state": ""
},
{
"label": "Nofollow",
"value": "0",
"state": ""
},
"…"
],
"cols": [
"Kind",
"Anchor text",
"URL",
"…"
],
"rows": [],
"notes": [
"This lists what the page declares. Whether those targets actually load is the broken-li…"
]
}
}/api/v1/tool?name=canonical 🔗 Which URL the page declares as original.GET /api/v1/tool?name=canonical&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "canonical",
"ms": 75,
"result": {
"summary": [
{
"label": "Canonical",
"value": "declared",
"state": "ok"
},
{
"label": "Self-referencing",
"value": "yes",
"state": "ok"
},
{
"label": "Declarations found",
"value": "1",
"state": "ok"
},
"…"
],
"cols": [
"Field",
"Value"
],
"rows": [
{
"a": "Requested URL",
"b": "https://astrina.io/",
"state": ""
},
{
"a": "URL after redirects",
"b": "https://astrina.io/",
"state": ""
},
{
"a": "Canonical in HTML",
"b": "https://astrina.io/",
"state": ""
},
"…"
],
"notes": []
}
}/api/v1/tool?name=http-status 📋 Bulk HTTP codes and redirects.GET /api/v1/tool?name=http-status&value=https%3A%2F%2Fastrina.io%2F%0Ahttps%3A%2F%2Fastrina.io%2Fpricing{
"ok": true,
"utility": "http-status",
"ms": 774,
"result": {
"summary": [
{
"label": "Checked",
"value": "2",
"state": "ok"
},
{
"label": "OK (2xx)",
"value": "2",
"state": "ok"
},
{
"label": "Redirected",
"value": "0",
"state": "ok"
},
"…"
],
"cols": [
"URL",
"Status",
"Hops",
"…"
],
"rows": [
{
"a": "https://astrina.io/",
"b": "200",
"c": "0",
"d": "same URL",
"e": "97 ms",
"state": "ok"
},
{
"a": "https://astrina.io/pricing",
"b": "200",
"c": "0",
"d": "same URL",
"e": "62 ms",
"state": "ok"
}
],
"notes": [
"All 2 addresses were checked, within the cap of 25 for this run."
]
}
}/api/v1/tool?name=favicon ⭐ Every size and format a browser looks for.GET /api/v1/tool?name=favicon&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "favicon",
"ms": 573,
"result": {
"summary": [
{
"label": "Icons declared",
"value": "1",
"state": ""
},
{
"label": "Icons that load",
"value": "1 / 2",
"state": "ok"
},
{
"label": "/favicon.ico",
"value": "missing",
"state": "warn"
},
"…"
],
"cols": [
"Relation",
"URL",
"Status",
"…"
],
"rows": [
{
"a": "icon",
"b": "https://astrina.io/favicon.svg",
"c": "200",
"d": "unknown",
"e": "396 B",
"state": "ok"
},
{
"a": "icon (browser fallback)",
"b": "https://astrina.io/favicon.ico",
"c": "404",
"d": "unknown",
"e": "2.8 KB",
"state": "warn"
}
],
"notes": [
"There is no /favicon.ico at the root. Browsers and feed readers still ask for it by def…",
"No apple-touch-icon. iOS will screenshot the page instead when someone adds it to their…",
"No web app manifest. Android uses it for the home-screen icon and the install prompt."
]
}
}🛡️ Security (12)
/api/v1/tool?name=ssl 🔒 Certificate, chain and expiry.GET /api/v1/tool?name=ssl&value=astrina.io{
"ok": true,
"utility": "ssl",
"ms": 86,
"result": {
"summary": [
{
"label": "Certificate",
"value": "valid and trusted",
"state": "ok"
},
{
"label": "Days left",
"value": "75",
"state": "ok"
},
{
"label": "Hostname matches",
"value": "yes",
"state": "ok"
},
"…"
],
"cols": [
"Field",
"Value"
],
"rows": [
{
"a": "Common name",
"b": "astrina.io",
"state": ""
},
{
"a": "Subject alternative names",
"b": "astrina.io, *.astrina.io",
"state": ""
},
{
"a": "Issued by",
"b": "WE1",
"state": ""
},
"…"
],
"notes": [
"Read from a live TLS handshake to 172.67.166.83:443 with SNI for astrina.io."
]
}
}/api/v1/tool?name=security-headers 🛡️ HSTS, CSP, frame options, graded.GET /api/v1/tool?name=security-headers&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "security-headers",
"ms": 94,
"result": {
"summary": [
{
"label": "Grade",
"value": "A",
"state": "ok"
},
{
"label": "Score",
"value": "85/100",
"state": "ok"
},
{
"label": "HSTS",
"value": "yes",
"state": "ok"
},
"…"
],
"cols": [
"Header",
"Value",
"Verdict"
],
"rows": [
{
"a": "Strict-Transport-Security",
"b": "max-age=31536000; includeSubDomains",
"c": "Set for 365 days.",
"state": "ok"
},
{
"a": "Content-Security-Policy",
"b": "default-src 'self' https: data:; script-src 'self' 'unsafe-inline' https:; style-src 's…",
"c": "Set. The CSP analyser tool checks whether it actually holds.",
"state": "ok"
},
{
"a": "X-Content-Type-Options",
"b": "nosniff",
"c": "Set correctly.",
"state": "ok"
},
"…"
],
"notes": [
"The grade weighs the headers that change what an attacker can do. It says nothing about…"
]
}
}/api/v1/tool?name=http-headers 📨 Every response header, explained.GET /api/v1/tool?name=http-headers&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "http-headers",
"ms": 88,
"result": {
"summary": [
{
"label": "Status",
"value": "200",
"state": "ok"
},
{
"label": "Protocol",
"value": "HTTP/2",
"state": "ok"
},
{
"label": "Headers",
"value": "20",
"state": ""
},
"…"
],
"cols": [
"Header",
"Value",
"What it does"
],
"rows": [
{
"a": "alt-svc",
"b": "h3=\":443\"; ma=86400",
"c": "Advertises another protocol for this origin, usually HTTP/3.",
"state": ""
},
{
"a": "cache-control",
"b": "no-store, no-cache, must-revalidate",
"c": "How long, and by whom, this response may be cached.",
"state": ""
},
{
"a": "cf-cache-status",
"b": "DYNAMIC",
"c": "",
"state": ""
},
"…"
],
"text": "alt-svc: h3=\":443\"; ma=86400\ncache-control: no-store, no-cache, must-revalidate\ncf-cach…",
"notes": [
"Our fetcher keeps one value per header name, so if the server sent several Set-Cookie l…"
]
}
}/api/v1/tool?name=csp 🚧 Names the directives that leave a hole.GET /api/v1/tool?name=csp&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "csp",
"ms": 116,
"result": {
"summary": [
{
"label": "Policy",
"value": "enforced",
"state": "ok"
},
{
"label": "Directives",
"value": "7",
"state": ""
},
{
"label": "Serious holes",
"value": "7",
"state": "bad"
},
"…"
],
"cols": [
"Directive",
"Sources",
"Finding"
],
"rows": [
{
"a": "default-src",
"b": "'self' https: data:",
"c": "data: in a script source is a known bypass; https: alone allows every https origin on t…",
"state": "bad"
},
{
"a": "script-src",
"b": "'self' 'unsafe-inline' https:",
"c": "'unsafe-inline' on scripts defeats the policy — this is the exact thing CSP exists to s…",
"state": "bad"
},
{
"a": "style-src",
"b": "'self' 'unsafe-inline'",
"c": "'unsafe-inline' — acceptable for styles, still worth removing",
"state": "warn"
},
"…"
],
"text": "default-src 'self' https: data:; script-src 'self' 'unsafe-inline' https:; style-src 's…",
"notes": [
"Nothing reports violations, so you will not learn what the policy blocked or nearly blo…",
"This reads the policy as written. Whether it fits what the page actually loads can only…"
]
}
}/api/v1/tool?name=dns 🌐 A, AAAA, MX, NS, TXT, SOA and CAA.GET /api/v1/tool?name=dns&value=astrina.io{
"ok": true,
"utility": "dns",
"ms": 507,
"result": {
"summary": [
{
"label": "A",
"value": "2",
"state": "ok"
},
{
"label": "AAAA",
"value": "2",
"state": "ok"
},
{
"label": "MX",
"value": "1",
"state": "ok"
},
"…"
],
"cols": [
"Type",
"Name",
"Value",
"…"
],
"rows": [
{
"a": "A",
"b": "astrina.io",
"c": "104.21.34.236",
"d": "292s",
"e": "",
"state": ""
},
{
"a": "A",
"b": "astrina.io",
"c": "172.67.166.83",
"d": "292s",
"e": "",
"state": ""
},
{
"a": "AAAA",
"b": "astrina.io",
"c": "2606:4700:3037::ac43:a653",
"d": "296s",
"e": "",
"state": ""
},
"…"
],
"notes": [
"No CAA record. Adding one names the certificate authorities allowed to issue for this d…",
"Answered by this server's resolver. A record changed in the last few hours may not have…"
]
}
}/api/v1/tool?name=whois 📜 Registrar, dates and nameservers.GET /api/v1/tool?name=whois&value=astrina.io{
"ok": true,
"utility": "whois",
"ms": 313,
"result": {
"summary": [
{
"label": "Domain",
"value": "astrina.io",
"state": "ok"
},
{
"label": "Registrar",
"value": "NameCheap, Inc.",
"state": ""
},
{
"label": "Expires",
"value": "2027-07-09",
"state": "ok"
},
"…"
],
"cols": [
"Field",
"Value"
],
"rows": [
{
"a": "Expires",
"b": "2027-07-09 15:37 UTC",
"state": ""
},
{
"a": "Registered",
"b": "2026-07-09 15:37 UTC",
"state": ""
},
{
"a": "Last changed",
"b": "2026-07-14 15:37 UTC",
"state": ""
},
"…"
],
"notes": [
"Read from the registry over RDAP, the protocol that replaced WHOIS. Registrant contact …"
]
}
}/api/v1/tool?name=email-auth ✉️ SPF, DKIM and DMARC records.GET /api/v1/tool?name=email-auth&value=astrina.io{
"ok": true,
"utility": "email-auth",
"ms": 1954,
"result": {
"summary": [
{
"label": "SPF",
"value": "present",
"state": "ok"
},
{
"label": "DKIM",
"value": "1 selector(s)",
"state": "ok"
},
{
"label": "DMARC",
"value": "p=none",
"state": "warn"
},
"…"
],
"cols": [
"Mechanism",
"Record",
"Finding",
"…"
],
"rows": [
{
"a": "MX",
"b": "mail.astrina.io",
"c": "priority 10",
"d": "",
"state": "ok"
},
{
"a": "SPF",
"b": "v=spf1 a mx ip4:149.202.80.42 ~all",
"c": "Ends in ~all (softfail) with about 2 lookups. Move to -all once you are confident the l…",
"d": "2 lookups",
"state": "ok"
},
{
"a": "DMARC",
"b": "v=DMARC1; p=none; rua=mailto:[email protected]; fo=1",
"c": "p=none — monitoring only. It collects reports and blocks nothing, which is the right pl…",
"d": "100%",
"state": "warn"
},
"…"
],
"notes": [
"Move from p=none to p=quarantine once the reports show your legitimate senders passing.…",
"All of this is read from public DNS. Nothing was sent to your mail servers and no messa…"
]
}
}/api/v1/tool?name=http2 ⚡ Which protocol and compression is used.GET /api/v1/tool?name=http2&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "http2",
"ms": 122,
"result": {
"summary": [
{
"label": "Protocol",
"value": "HTTP/2",
"state": "ok"
},
{
"label": "Compression",
"value": "Zstandard",
"state": "ok"
},
{
"label": "HTTP/3",
"value": "advertised",
"state": "ok"
},
"…"
],
"cols": [
"Measure",
"Value",
"What it means"
],
"rows": [
{
"a": "Negotiated protocol",
"b": "HTTP/2",
"c": "Multiplexed: many requests share one connection, so head-of-line blocking on the browse…",
"state": "ok"
},
{
"a": "Compression",
"b": "Zstandard",
"c": "Zstandard — fast and compact where the client supports it.",
"state": "ok"
},
{
"a": "Alt-Svc",
"b": "h3=\":443\"; ma=86400",
"c": "HTTP/3 is advertised, so browsers will move to QUIC after the first request.",
"state": "ok"
},
"…"
],
"notes": [
"We ask for every encoding we can accept, so what is reported is what your server chose …"
]
}
}/api/v1/tool?name=ip-info 📍 Geolocation and network for an address.GET /api/v1/tool?name=ip-info&value=astrina.io{
"ok": true,
"utility": "ip-info",
"ms": 2145,
"result": {
"summary": [
{
"label": "Host",
"value": "astrina.io",
"state": ""
},
{
"label": "Addresses",
"value": "4",
"state": "ok"
},
{
"label": "IPv6",
"value": "yes",
"state": "ok"
},
"…"
],
"cols": [
"Address",
"Family",
"Network",
"…"
],
"rows": [
{
"a": "104.21.34.236",
"b": "IPv4",
"c": "AS13335 — CLOUDFLARENET - Cloudflare, Inc., US",
"d": "104.21.32.0/19",
"e": "US",
"f": "no reverse DNS",
"state": "warn"
},
{
"a": "172.67.166.83",
"b": "IPv4",
"c": "AS13335 — CLOUDFLARENET - Cloudflare, Inc., US",
"d": "172.67.160.0/20",
"e": "US",
"f": "no reverse DNS",
"state": "warn"
},
{
"a": "2606:4700:3035::6815:22ec",
"b": "IPv6",
"c": "AS13335 — CLOUDFLARENET - Cloudflare, Inc., US",
"d": "2606:4700:3035::/48",
"e": "US",
"f": "no reverse DNS",
"state": "warn"
},
"…"
],
"notes": [
"4 addresses answer for this name. That is normal for a CDN or a load balancer.",
"The country shown is the one recorded on the address allocation in the routing registry…",
"No commercial geolocation database was consulted, so there is no city, no coordinate an…"
]
}
}/api/v1/tool?name=ports 🔌 Well-known service ports, checked safely.GET /api/v1/tool?name=ports&value=https%3A%2F%2Fexample.com%2F{ "ok": true, "utility": "ports", "ms": 0, "result": { … } }/api/v1/tool?name=cookies 🍪 Cookies and their flags against GDPR.GET /api/v1/tool?name=cookies&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "cookies",
"ms": 103,
"result": {
"summary": [
{
"label": "Cookies in response",
"value": "1",
"state": "ok"
},
{
"label": "Third-party trackers",
"value": "0",
"state": "ok"
},
{
"label": "Consent mechanism",
"value": "detected",
"state": "ok"
},
"…"
],
"cols": [
"Name",
"Value",
"Secure",
"…"
],
"rows": [
{
"a": "astrsess",
"b": "44c774bdb4b0b81bd429e64545acc9a7",
"c": "yes",
"d": "yes",
"e": "lax",
"f": "session",
"g": "flags look right",
"state": "ok"
}
],
"notes": [
"Cookies set later by JavaScript, or after a login, are outside what a single anonymous …"
]
}
}/api/v1/tool?name=cors 🔓 Which origins your endpoint allows.GET /api/v1/tool?name=cors&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "cors",
"ms": 884,
"result": {
"summary": [
{
"label": "CORS enabled",
"value": "no",
"state": "ok"
},
{
"label": "Allowed origin",
"value": "same origin only",
"state": "ok"
},
{
"label": "Origin reflection",
"value": "none",
"state": "ok"
},
"…"
],
"cols": [
"Header or probe",
"Value",
"Meaning"
],
"rows": [
{
"a": "Access-Control-Allow-Origin",
"b": "— not sent —",
"c": "No CORS headers on a request that carried no Origin. That is the ordinary case; what ma…",
"state": "ok"
},
{
"a": "Access-Control-Allow-Credentials",
"b": "— not sent —",
"c": "Credentials are not shared.",
"state": "ok"
},
{
"a": "Access-Control-Allow-Methods",
"b": "— not sent —",
"c": "Sent on preflight responses.",
"state": ""
},
"…"
],
"notes": [
"Two forged origins were sent and neither was echoed back, so this endpoint does not bui…",
"This is a GET carrying an Origin header, not a full preflight. An endpoint that answers…"
]
}
}🎨 CSS (1)
/api/v1/tool?name=palette-url 🌐 The colours a site actually uses.GET /api/v1/tool?name=palette-url&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "palette-url",
"ms": 578,
"result": {
"summary": [
{
"label": "Distinct colours",
"value": "132",
"state": "warn"
},
{
"label": "Stylesheets read",
"value": "2 / 2",
"state": "ok"
},
{
"label": "CSS custom properties",
"value": "28",
"state": "ok"
},
"…"
],
"cols": [
"Colour",
"RGB",
"Uses",
"…"
],
"rows": [
{
"a": "#ffffff",
"b": "rgb(255, 255, 255)",
"c": "40",
"d": "near-white",
"e": "contrast on white 1.0:1 · on black 21.0:1",
"state": ""
},
{
"a": "#000000",
"b": "rgb(0, 0, 0)",
"c": "27",
"d": "near-black",
"e": "contrast on white 21.0:1 · on black 1.0:1",
"state": ""
},
{
"a": "#5b5bd6",
"b": "rgb(91, 91, 214)",
"c": "20",
"d": "brand or accent",
"e": "contrast on white 5.4:1 · on black 3.9:1",
"state": ""
},
"…"
],
"notes": [
"Over 60 distinct colours. That is usually several palettes layered by different people …",
"Counts are how often a colour appears in the CSS, which is not the same as how much of …"
]
}
}{ } DevOps (1)
/api/v1/tool?name=php-version 🐘 Detect the PHP version a server reveals.GET /api/v1/tool?name=php-version&value=https%3A%2F%2Fastrina.io%2F{
"ok": true,
"utility": "php-version",
"ms": 74,
"result": {
"summary": [
{
"label": "PHP version",
"value": "not disclosed",
"state": "ok"
},
{
"label": "Security support",
"value": "unknown",
"state": ""
},
{
"label": "Version leaked",
"value": "no",
"state": "ok"
}
],
"cols": [
"Field",
"Value",
"Verdict"
],
"rows": [
{
"a": "PHP detected",
"b": "no",
"c": "Nothing in the response names PHP. The site may not use it, or it may simply be configu…",
"state": "ok"
},
{
"a": "Server header",
"b": "cloudflare",
"c": "No version published here.",
"state": "ok"
}
],
"notes": [
"This reads only what the server volunteers in its headers. Nothing is probed, and a sit…"
]
}
}Login with Astrina (Auth API)
Add social login to your site with one integration. Astrina is the OAuth2/OIDC-style broker; you register your site once and reuse every provider you enable.
GET /auth/authorize.php | Send the user here (add &provider=google to skip the chooser). Returns a code to your redirect_uri. |
POST /auth/token.php | Exchange the code (server-side, with your client secret) for the identity: sub, provider, email, name, avatar. |
GET /auth/providers.php | List the providers currently enabled, with ready deep-link login URLs. |
Drop-in PHP SDK + full guide: integration guide · download sdk.php
Image optimization API
Compress and convert images (JPEG, PNG, GIF, WebP, AVIF, SVG) — lossless or lossy. Send a file, a URL or base64; get back the optimized image and its size saving.
POST /api/v1/image | Body: image=@file, or JSON {url} / {data:base64}. Options: lossy=1|0, format=keep|webp|avif|jpeg|png, quality, resize. Returns in/out bytes, saved_percent and a download url. |
GET /api/v1/image?get=… | Fetch an optimized result by its token — no key needed, hand the url straight to an image tag. |
Paid plans only; metered against your monthly image quota. Web interface: Image optimizer
Endpoints you do not call
Two families exist under /api/v1 that are not for your code, listed here so this page is the complete map:
POST /api/v1/social/inbound | The provider webhook receiver (Meta / WhatsApp Cloud API). It is called BY the provider, not by you: GET is the subscription handshake, POST delivers messages. Configure it in Social. |
/api/v1/node/… | The Pulsar device protocol — register, next, submit, heartbeat and friends — spoken by the node agent, not by an API key. It is documented with the rest of the protocol on the Pulsar protocol page. |
Site groups
/api/v1/groups Your site groups and their members — the group ids that /api/v1/stats?group=… takes, without scraping the dashboard.GET /api/v1/groups
X-Api-Key: ak_your_key_here
{ "ok":true, "groups":[ { "id":4, "name":"Client sites", "sites":[ {"id":12,"domain":"example.com"} ] } ] }Read-only; groups are edited in My site groups. Scope groups.
Promotion snapshot
/api/v1/promotion?site=… The promotion picture of one of your sites as JSON: rankings, geo, backlinks, audit and goals — the same numbers the promotion hub shows.| Param | Description |
|---|---|
site | site id or domain — must belong to the key’s owner |
section | summary (default) · rankings · geo · backlinks · audit · goals |
A field the platform has no data for comes back null — never a guess. Scope promotion.
Pulsar network data
/api/v1/pulsar?site=… Distributed monitoring for a site you own, gathered by independent devices: per-monitor confirmation from several vantage points, and latency by country.| Param | Description |
|---|---|
site | site id or domain (required) |
report | monitors (default) · coverage |
Every figure is a quorum verdict from independent devices; when the network has not decided yet the value is null rather than a single server’s opinion. About the network.
Comments
/api/v1/comment?key=… Moderated on-site comments for any page, by URL. The same store the drop-in comment widget uses.GET /api/v1/comment?key=https://example.com/post POST /api/v1/comment key=https://example.com/post&author=Ann&body=Nice+one
Bodies are stored as plain text (tags stripped) — escape on render. Moderation and the widget live in Comments.
Threat-intel export
/api/v1/ti-export Licence-aware export of the threat-intelligence corpus: only redistributable-licensed or Astrina-originated rows, each carrying its source for attribution.Double-gated: a valid key AND the operator switch, which is off until the commercial terms are agreed. Restrictively-licensed feeds are never included, whatever the plan.
Get your API key
What this page answers
- API endpoint reference
- API authentication and quotas
- API request examples
- REST JSON API documentation
- how to get an API key
- what is the astrina api
- how the astrina api works
- the astrina api explained
- the astrina api guide
- how to start with the astrina api
- the astrina api reviews
- is the astrina api worth it
- the astrina api pricing
- the astrina api alternatives
- the astrina api for beginners
- why the astrina api
- the astrina api — what you get
Social API