Skip to content

SEO remediation (Ahrefs 2026-07-30): site-wide Open Graph + ranking recovery - #226

Merged
buddies2705 merged 3 commits into
mainfrom
fix/seo-og-and-links
Jul 30, 2026
Merged

SEO remediation (Ahrefs 2026-07-30): site-wide Open Graph + ranking recovery#226
buddies2705 merged 3 commits into
mainfrom
fix/seo-og-and-links

Conversation

@buddies2705

Copy link
Copy Markdown
Contributor

Remediation of the 2026-07-30 Ahrefs Site Audit of docs.bitquery.io.

Two phases in one branch — reviewable as two independent commits:

Commit Scope
4985eac4 Phase 1 — config + mechanical. Clears both Errors and ~578 Warnings.
e1039686 Phase 2 — ranking recovery on the 9 pages that lost positions.

Correcting the export's headline numbers first

The CSVs are UTF-16/TSV with newlines inside multi-value cells, so wc -l overstates record counts ~9×. "5,157 Open Graph issues" is 573; "3,604 IndexNow" is 572. Real totals collapse to three root causes, only one of which is in this repo.

Phase 1 — config + mechanical

  • og:type + og:site_name in headTags. Docusaurus emits og:title/description/image/url/locale and neither of these, so all 573 indexable pages failed Open Graph validation. One config change. Verified: 1019 built pages carry both; the 245 without are client-redirect stubs, which correctly have no OG block.
  • Sitemap contained a redirect. createSitemapItems hardcoded /crypto-reward-tax-calculator/ with a trailing slash; that path is a separate Next.js app whose normalization 308s to the unslashed form.
  • Two http:// internal links → https. The v1 link on Pump-Fun-API alone tripped 4 separate reports. Both support.bitquery.io links on how-to-filter-anomaly-prices (two occurrences, not one).
  • alt text on 4 images, written from the actual screenshots.
  • Polymarket generated-index retitled. This index — not its child page — is what ranks (position 2 for polymarket api, 863 organic). Google was discarding "Polymarket Implementation Guide" and substituting the child page's title verbatim. Description was 95 chars.

Phase 2 — ranking recovery

All 9 pages verified technically clean first (200, self-canonical, no noindex), so these are content and internal-linking problems, not indexation. Every documented query was run against the live API before being written.

Defects found by reading the pages, absent from the Ahrefs export

  • solana_dexscreener shipped unparseable JSON""So111… (doubled quote) and an unterminated "pair_address key. Copy-paste failed outright.
  • matic-dextrades promised what it never delivered — the callout routed readers there for history "with dataset: combined or archive" while dataset appeared nowhere else in the file.
  • The TRC20 flagship query fails server-sideupstream: tron: backend … context deadline exceeded after 3 minutes, under a heading claiming to answer the most-asked USDT question on Tron.
  • Six dead example tokens across two pages — AVAX ($10k/30d), IXT ($427/30d), BILLY (12 trades/30d), AUTISM (9), Pnut (1).
  • Stale hardcoded query variables (2024-08-15, 2024-11-06, 2024-11-13) — copy-pasting requested a ~20-month window. Now since_relative.
  • Pumpfun cannibalizationPump-Fun-API had no keywords at all while the hub claimed "Best PumpFun API", and pump-fun-to-pump-swap, the page hoarding four terms it shouldn't own, was the only page in the cluster that ranked.

Two items where my own analysis was wrong

  • four-meme-api has no payload problem. It ships 42 KB gzipped; the 728 KB I flagged was uncompressed DOM, and peers are larger (solana-dextrades 892 KB). Ahrefs' "Size (bytes) 42372" was already the compressed figure.
  • pancake-swap-api inlinks were understated — 6 in-repo references, not 3. No action taken rather than adding decorative links.

Also documented

  • MATIC→POL rebrand: wrapped native now reports WPOL, Tether as USDT0 — filter by contract, not symbol.
  • Fungible: true on Polygon rankings, or Polymarket ERC-1155 outcome tokens dominate with empty symbols.
  • UpdateCount as a wallet classifier: 1.2M updates in ~10 days (exchange hot wallet) vs 5 on a wallet holding 87.5M USDT (cold storage).
  • Memecoin pools decay within hours — one candidate went 34,325 trades/2h → 28 trades/1h while being tested — and aggregators (Jupiter, dflow, Aquifer) expose no MarketAddress.
  • Balances preferred over BalanceUpdates throughout.

Verification

  • yarn build passes; build output confirmed to reflect the changes (not a cache hit)
  • scripts/check-links.mjs: no link errors (532 pages, 1294 routes, strict)
  • 151/151 internal /docs links in changed files resolve
  • 17 JSON blocks parse (one deliberate reader placeholder excepted)

Deliberately not in this PR

  • V1 docs site (409 pages). Every /v1/** page emits canonical → /v1/, plus one shared 108-char title and one shared description across all 409, and double-fired GA4. Separate deployment — needs a handoff to that repo. Our robots.txt should not declare /v1/sitemap.xml until that canonical is fixed.
  • crypto-reward-tax-calculator — no OG, no X card, zero outlinks. Separate Next.js app.
  • 5 "Slow page" warnings (crawl artifact — Ahrefs logged 13–19 s TTFB; live is 0.58–0.61 s), 572 IndexNow rows (upsell), 91 "changed" notices (prior audit work landing), 6 AI-content flags (Ahrefs' classifier; 5 of 6 have zero traffic).
  • twitter:site — omitted rather than guess a live social handle.

Open items

  • Holders aggregate stats (sum/median/gini/nakamoto) returned total: 0 and median: 0 on a token whose top holder has 18M. Left out of the docs; worth an API-team look.
  • Templated descriptions down 145 → 138. The rest is Phase 3.
  • BalanceUpdates still appears in ~70 docs files.

🤖 Generated with Claude Code

Gaurav agarwal and others added 3 commits July 30, 2026 15:48
From the 2026-07-30 Ahrefs crawl. Clears both Errors and ~578 Warnings.

- og:type + og:site_name in headTags. Docusaurus emits og:title/description/
  image/url/locale but neither of these, so all 573 indexable pages failed
  Open Graph validation. Verified: 1019 built pages now carry both (the 245
  without are client-redirect stubs, which correctly have no OG block).
- Sitemap listed /crypto-reward-tax-calculator/ with a trailing slash. That
  path is a separate Next.js app whose normalization 308s to the unslashed
  form, so we were publishing a redirect in our own sitemap.
- Two http:// internal links -> https: the v1 link on Pump-Fun-API (which
  alone tripped 4 separate reports) and both support.bitquery.io links on
  how-to-filter-anomaly-prices.
- alt text on 4 images, written from the actual screenshots.
- Polymarket generated-index retitled. This index, not its child page, is
  what ranks (position 2 for "polymarket api", 863 organic). Google was
  discarding "Polymarket Implementation Guide" and substituting the child
  page's title, so lead with the head term. Description was 95 chars.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lize Pumpfun

Phase 2 of the 2026-07-30 Ahrefs remediation. Covers the 9 pages that lost
rankings. All 9 verified technically clean first (200, self-canonical, no
noindex), so these are content and internal-linking problems, not indexation.
Every documented query was run against the live API before being written.

matic-dextrades (1267 -> 1892 words)
- Added the archive/history section the page already promised: the callout
  routed readers here for data older than the Trading window "with
  dataset: combined or archive", but `dataset` appeared nowhere else in the
  file. Verified dataset: archive returns 2025-01-01 trades.
- Added OHLC via Trading.Tokens (Interval.Time.Duration).
- Replaced both example tokens. AVAX (~$10k/30d) and IXT (~$427/30d, ~$0.19
  per trade) were effectively dead; now WPOL/USDC and WETH.
- Replaced hardcoded 2024-11-13 / 2024-11-10 variables with since_relative.
  Copy-pasting the old top-gainers query requested a ~20-month window.
- Documented the MATIC->POL rebrand (wrapped native now reports WPOL, Tether
  as USDT0) and to filter by contract, not symbol.
- Documented Fungible: true, without which Polymarket ERC-1155 outcome tokens
  dominate any Polygon volume ranking with empty symbols.

usdt-trc20-api (650 -> 1689 words, 0 -> 2 inlinks)
- Retitled from "Tron Usdt Trc20 API"; H1 now matches the title.
- Replaced the top-100-holders query: it fails server-side
  ("upstream: tron: backend ... context deadline exceeded") after 3 min, under
  a heading claiming to answer the most-asked USDT question on Tron. Now uses
  Balances per-address, plus Holders with an amount floor for a whale list.
  Both verified. Balances is preferred over BalanceUpdates throughout.
- Documented UpdateCount as a wallet classifier: 1.2M updates in ~10 days
  (exchange hot wallet) vs 5 updates on a wallet holding 87.5M USDT (cold).
- Added whale transfers, top receivers, and a single-request cross-chain
  comparison across Tron/Ethereum/BSC/Polygon.
- Kept cross-chain depth on stablecoin-APIs/usdt-api rather than duplicating
  it here; added reciprocal links.

Pumpfun cluster
- Assigned keyword ownership per page. Pump-Fun-API had no keywords at all
  while the hub claimed "Best PumpFun API", and pump-fun-to-pump-swap -- the
  page hoarding four terms it should not own -- was the only page in the
  cluster that ranked. All five descriptions were near-duplicates; now
  distinct. Added a "which page do I need" router to the hub.
- Trading API callout kept at the top on all pages, reframed as a positive
  router instead of reading as "do not use this page".

solana_dexscreener
- Fixed unparseable JSON in the pair-stats variables ('""So111...' and an
  unterminated "pair_address key). Copy-paste failed outright.
- Replaced 2024-08-15 / 2024-11-06 dates with since_relative/after_relative.
- Replaced three dead example mints (BILLY 12 trades/30d, AUTISM 9, Pnut 1)
  with the WSOL/USDC Orca Whirlpool, and documented why: memecoin pools decay
  within hours (one candidate went 34,325 trades/2h to 28 trades/1h while
  being tested), and aggregators like Jupiter expose no MarketAddress.

erc20-token-transfer-api: de-templated description, added keywords.

No action, with reasons:
- BSC/four-meme-api: no payload problem. It ships 42 KB gzipped; the 728 KB
  figure was uncompressed DOM, and peers are larger (solana-dextrades 892 KB).
- BSC/pancake-swap-api: already has 6 in-repo inlinks, not 3.
- start/errors: no commercial intent.

Not documented: Holders aggregate stats (sum/median/gini/nakamoto) returned
total: 0 and median: 0 on a token whose top holder has 18M. Needs API-team
confirmation before use.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ueries

Deprecated-cube cleanup, driven by live schema introspection rather than
find-and-replace. The schema's own deprecationReason strings are the source
of truth here.

State verified against the live API on 2026-07-30:
- EVM.TokenHolders  -> BROKEN. Fails on realtime ("no table can query
  TokenHolder ... consider use archive dataset") and on archive ("Database eth
  does not exist"). Every documented query using it was dead.
- EVM.BalanceUpdates -> deprecated but STILL EXECUTES (returned data stamped
  2026-07-30T11:38:59Z).
- Tron.BalanceUpdates -> deprecated but STILL EXECUTES.
- Solana.BalanceUpdates / InstructionBalanceUpdates -> NOT deprecated. Solana
  has no Balances cube, so these are the current APIs there and must stay.

New: docs/cubes/balances-cube.md
Eight pages already tell users to use EVM.Balances, but no cube reference
documented it. Adds Balances (per-address current state) vs Holders (a token's
holders, dated snapshot), field lists, dataset guidance, a BalanceUpdates
migration mapping, and two verified gotchas:
- Balances has no amount filter (where accepts Balance.Address only), so
  ranking by balance size requires Holders.
- Unbounded top-N over a very large token times out server-side. Ethereum USDT
  times out even with a 50,000,000 floor; Tron USDT behaves the same.

Migrated 16 TokenHolders sites to Holders across 7 files: rwa-api, pepe-api,
token-holder-api, optimizing-graphql-queries, metrics/uniq,
stablecoin-payments-api, tokenholder-heatmap (inside an escaped-JSON payload).
The argument shape changes, not just the name: `tokenSmartContract: X` becomes
`where: { Currency: { SmartContract: { is: X } } }`. Verified by extracting a
migrated block verbatim from metrics/uniq.md and running it: 1,378,728 exact
holders returned.

balance-updates-cube.md had no deprecation notice at all despite documenting a
cube deprecated on EVM and Tron. Added one that also states why the page is
still the right reference: Solana, and change attribution -- Balances gives the
resulting amount but only BalanceUpdates exposes Type (transfer / fee /
block_reward) per change.

Corrected stale prose on token-holder-api.mdx that offered the "legacy
TokenHolders cube" as a working option for distribution metrics.

Not migrated, deliberately:
- NFT ownership queries (nft-ownership-api, nft-api, nft-collection-api,
  nft-scanner, cross-chain, nft-creator). Balances CANNOT express per-token-ID
  ownership: its filter accepts Address only, there is no Id field, and NFT
  collections return Amount: "0" aggregate rows. These need BalanceUpdates
  (which exposes BalanceUpdate.Id) or Transfers.
- graphql-reference/objects/evm.mdx: auto-generated schema reference.
- Solana pages: current API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@buddies2705
buddies2705 merged commit 67659fd into main Jul 30, 2026
1 check passed
buddies2705 added a commit that referenced this pull request Jul 30, 2026
…it-fixes

SEO Phase 3/4 + fix broken Holders queries shipped in #226
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant