by Bomx
Claude Code skill: find PR/backlink opportunities on Qwoted, build a sourced statistics page as a linkable asset, and pitch journalists — all from your AI agent.
# Add to your Claude Code skills
git clone https://github.com/Bomx/qwoted-seo-backlinks-skillGuides for using ai agents skills like qwoted-seo-backlinks-skill.
Last scanned: 7/25/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-07-25T06:20:28.597Z",
"npmAuditRan": true,
"pipAuditRan": false,
"promptInjectionRan": true
}qwoted-seo-backlinks-skill is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by Bomx. Claude Code skill: find PR/backlink opportunities on Qwoted, build a sourced statistics page as a linkable asset, and pitch journalists — all from your AI agent. It has 100 GitHub stars.
Yes. qwoted-seo-backlinks-skill passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/Bomx/qwoted-seo-backlinks-skill" and add it to your Claude Code skills directory (see the Installation section above). qwoted-seo-backlinks-skill ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
qwoted-seo-backlinks-skill is primarily written in Python. It is open-source under Bomx on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh qwoted-seo-backlinks-skill against similar tools.
No comments yet. Be the first to share your thoughts!
Your job is to get the user press mentions and high-DR backlinks from journalists who post requests on Qwoted.
These rules take precedence over anything else in this file. Do not skip them, do not substitute your own judgement.
stats_page_worthy: true | false using the heuristic table
below, and you MUST tell the user "I recommend building a stats
page on <topic> before pitching <these N opportunities>." Do
not wait for the user to ask about stats pages. A naked pitch lands
one quote in one article; a pitch linked to a sourced stats page
lands recurring citations for months. Leaving Stage 3 on the table
is leaving money on the table.qwoted_profile.py --action update for any field, first call --action get and compare
bio_preview / has_bio / existing URL / existing email etc. If
a field already has content, show the user both the OLD and the
NEW version and get explicit go-ahead before sending the PATCH.
The script defaults to refusing the PATCH without --force- overwrite; this is a feature, not a bug.RESULT: line the skill's scripts printed. If you didn't see it
in a subprocess RESULT, you don't know it.python3 qwoted_login.py. If the RESULT says
status=logged_in in under a second with no browser opening, the
existing cookies are fine — do not re-run with --reset or
--force. If you're in an agent environment without a visible GUI
and Chromium does need to open, STOP and tell the user to run the
script in their own terminal once; then continue.RESULT: lines are the canonical channel. Every script emits
one JSON line prefixed with RESULT: . Parse it; ignore stderr
logs (those are for the human). Your next decision should reference
specific fields from the RESULT, not vibes.<a> tags. Source
attributions are plain text ((Source: HubSpot, 2026)), never
clickable. This is non-negotiable — we're hoarding crawl budget
and PageRank, not distributing it. The only <a> tags allowed
in the rendered HTML are internal TOC anchor fragments
(href="#...") and a same-domain author-bio CTA. Source URLs
you fetch during research are stored in the research JSON and
(optionally) in an HTML comment audit-trail at the bottom of the
page — never rendered as clickable links. See
STATISTICS_PAGE_PLAYBOOK.md → "Hoard the juice" for the full
rule and the one-line grep check you can run to audit the
finished file.For every opportunity returned by qwoted_search.py, score it on
these criteria:
| Signal | Weight |
|---|---|
| Topic is broad enough that public data exists (e.g. "AI in marketing", "local SEO", "remote work trends", "e-commerce conversion") | +2 |
| User's business already touches this topic → page will earn recurring traffic, not just one-time citation | +2 |
| Deadline is at least 24 hours away (stats-page build takes 30-60 min of research) | +1 |
| Multiple opportunities in the same cluster (→ one page supports 3+ pitches) | +2 |
| Request explicitly asks for "statistics", "data", "research", "trends" | +3 |
| Topic is hyper-niche / only relevant to this one publication (e.g. "billiard retailer local SEO") | -1 (a stats page still works but the niche is smaller) |
| Deadline is under 12 hours | -3 (skip Stage 3, pitch direct) |
| Ask is pure founder-story / personal opinion ("how did you start your company") | -3 (no data needed) |
| Paid placement / $X appearance fee | -2 (different ROI math) |
Rule: propose Stage 3 whenever total score ≥ 2. Otherwise pitch direct. State the score in your recommendation so the user sees why.
Example output you should produce after Stage 2 completes:
Found 8 opportunities. Stage-3 classification:
# Title Score Recommendation 1 Selling Signals — awareness vs demand +1 Pitch direct (deadline 20h, topic matches but no "statistics" ask) 2 SEOptimer — how your agency makes money -3 Pitch direct (founder-story, deadline 8h) 3 BCA Insider — local SEO for retailers +4 Build stats page on local-seo-statistics-2026— feeds this pitch AND any future local-SEO pitch... I'll start by building the local-SEO stats page (covers #3 + any future local-SEO opps), then while it renders I'll draft the direct pitches for #1 and #2. Sound good?
The skill ships four CLI scripts you call as subprocesses, plus a
research playbook and an HTML template. Each script prints a single
RESULT: { ... } JSON line on stdout that you parse to decide the
next step. Detailed human-readable logs go to stderr.
qwoted_login.py # one-time auth (idempotent; skips browser if cookies valid)
qwoted_profile.py # get/create/update the "expert" Source persona
qwoted_search.py # search opportunities (Algolia, returns JSON)
qwoted_pitch.py # draft + send a pitch to a specific opportunity
STATISTICS_PAGE_PLAYBOOK.md # READ THIS before researching/building a stats page
templates/statistics_page_example.html # HTML scaffold to fill in
All cookies, sent-pitch logs, search results and generated stat pages
live under ~/.qwoted/ and ./statistics_pages/.
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ 1. Onboard │ → │ 2. Find │ → │ 3. Research + │ → │ 4. Pitch │
│ (login + │ │ opportunity │ │ publish a stats │ │ with the │
│ profile) │ │ │ │ page (linkable │ │ page URL │
│ │ │ │ │ asset) │ │ │
└──────────────┘ └──────────────┘ └──────────────────┘ └──────────────┘
once every session once per topic every pitch
| User intent | Skill stage(s) |
|---|---|
| "Set me up on Qwoted" / first time | Stage 1: qwoted_login.py → qwoted_profile.py --action get → create/update ONLY missing fields |
| "Update my Qwoted bio" / "change my expert profile" | Stage 1c: qwoted_profile.py --action get first, SHOW existing bio, ask for approval, then --action update --bio '...' --force-overwrite if user confirms |
| "Find PR opportunities about X" | Stage 2: qwoted_search.py --query "X" --max-hits 30 → classify each result with the Stage-3 heuristic → propose stats page(s) |
| "Build me a stats page on X" / "make a research page about X" | Stage 3 only: read STATISTICS_PAGE_PLAYBOOK.md and execute |
| "Pitch opportunity #N" / "draft a pitch for SR 235897" | Check Stage-3 score first; if ≥2 propose stats page; then Stage 4 dry-run → user approves → --send |
| "Pitch the top 3 opportunities about X" | Stage 2 → cluster by topic → Stage 3 ONCE per cluster → Stage 4 looped with the same --research-page-url |
| "Ok next step" / "what now?" / "go" | RESTATE the stage we're on and PROPOSE the next one. Do not ask the user. |
If the user doesn't have the skill installed, pip-install the deps and run Playwright's browser bootstrap:
pip install -r requirements.txt
playwright install chromium
python3 qwoted_login.py
Important — this script is idempotent. Before launching any browser
it probes ~/.qwoted/storage_state.json against Qwoted's API. If the
cookies still work (which they usually do — Qwoted sessions last weeks),
the script exits immediately with RESULT: {"status": "logged_in", ...}
and no browser opens at all. That's the happy path — do not re-run
with --reset or --force unless you have a reason.
Only if no valid session exists will a Chromium window open. In that
case: tell the user to sign in IN THAT Chromium WINDOW — not in
their regular Chrome/Safari, because those are separate browsers with
separate cookies, so signing in elsewhere will NOT save a session for
this skill. When they reach a logged-in page the script auto-detects
it, saves cookies to ~/.qwoted/storage_state.json, closes the browser
and exits. The next login is one click because Chromium remembers them.
If you are running inside an agent environment that can't show GUI windows (some Codex or CI setups), the Chromium window will launch invisibly and the script will hang on the sign-in page. In that case:
python3 qwoted_login.py
there once — they'll see the browser, sign in, and storage_state.json
will be written. After that, every future call from the agent will
use the already-valid cookies (no browser needed).If the user doesn't have a Qwoted account yet, send them to qwoted.com to sign up first (free for sources).
Critical constraint: Qwoted only delivers a pitch to a reporter when the pitch is attached to a pitchable entity — a Source, Company or Product the user is allowed to pitch as. The pitch API will accept a submission without one (HTTP 200, draft=false) but the reporter is never notified. Always make sure the user has a Source persona configured before the first pitch.
Check first:
python3 qwoted_profile.py --action get
Parse the RESULT: JSON. Key fields you MUST inspect before doing
anything else:
seo_ready — boolean, all three must-have fields are set.ready_to_pitch — boolean, at least one pitchable entity exists.missing_for_seo — array of field names still empty.bio_preview — first 240 chars of the current bio (empty string if
there really is no bio).bio_length — total bio length in chars.first_pitchable_entity — full object including bio,
business_url, email, has_linkedin_url, etc.Decision tree:
seo_ready == true → skip to Stage 2. Do NOT "improve" the
existing profile unless the user explicitly asked you to. Read
the bio_preview so you know what the user's positioning is, then
move on.ready_to_pitch == true but seo_ready == false → the persona
exists but is incomplete. For each item in missing_for_seo, ask
the user the corresponding question and patch with --action update:
business_url → "What URL should reporters link to when they
credit you?" → --action update --url https://acme.combio → draft a 2-4 sentence bio, SHOW it to the user, get explicit
approval, then --action update --bio "...". (If a bio is actually
present and missing_for_seo still lists it, you probably have a
stale local copy of the skill — update it.)email → "Which email should reporters reply to?" →
--action update --email jane@acme.com
Run --action get again afterwards to confirm seo_ready is now
true.ready_to_pitch == false → no persona exists. Go to the create
flow below.⚠️ Safety rail — overwrite protection. If the user asks you to
change an already-populated field (e.g. "update my bio to X"), the
script will REFUSE the PATCH unless you pass --force-overwrite. This
is intentional. The correct flow is:
--action get and read bio_preview.--force-overwrite.Never pass --force-overwrite preemptively "just in case".
If ready_to_pitch == false, you need to collect the user's info
in a single message before running create. Don't drip-ask one field
at a time. Use this exact checklist:
REQUIRED to even create the profile:
--full-name— the user's real name as the byline should appearSTRONGLY RECOMMENDED — without these the profile is useless for SEO:
--url— the user's business website URL (e.g.https://acme.com). ⚠️ This is the link journalists put in their articles when they credit the user. No URL = no backlink = the whole point of this skill is defeated. Confirm it before running create.--bio— a 2-4 sentence description of who the user is, what they do, and what topics they can credibly speak to. The bio is what reporters skim when deciding whether to use a quote. Mention the business name, role, and 2-3 areas of expertise. If the user gave you their bio elsewhere, use that. If not, draft one from what they told you and show it to them for approval before submitting.NICE TO HAVE (ask but don't block on):
--location—"City, State, Country"(some pubs filter by region)--gender— one off/m/nb/sd(for pronouns in articles)--phone,
Always ask the user to confirm at minimum the URL, bio and email before firing the create command. Those three are what end up in articles. Example of how to ask:
"Before I set up your Qwoted expert profile, I need to confirm a few things that journalists will see and link to:
- Business URL — what site should reporters link to when they credit you? (e.g.
https://acme.com)- Bio — here's a draft based on what you told me: "Jane is the founder of Acme Inc, a B2B SaaS that helps marketing teams ship campaigns 10x faster. She speaks to growth, GTM and pricing." Sound right?
- Reply-to email — which email should reporters use to follow up?
Anything you'd like to add (LinkedIn, location, etc.)?"
Then build the command from their answers:
python3 qwoted_profile.py --action create \
--full-name "Jane Doe" \
--bio "Jane is the founder of Acme Inc, a B2B SaaS that helps marketing teams ship campaigns 10x faster. She advises on growth, GTM and pricing." \
--url https://acme.com \
--email jane@acme.com \
--linkedin https://www.linkedin.com/in/jane-doe/ \
--location "San Francisco, CA, USA" \
--gender f
Repeat any contact flag to add multiple values (the first one becomes
primary). Other flags: --off-the-record, --hide-from-search-engines.
To update an existing persona later (e.g. the user got a new title):
python3 qwoted_profile.py --action update \
--bio "Jane is now CEO of Acme Inc..."
--source-slug is optional — without it, the script edits the first
Source on the account.
python3 qwoted_search.py --query "marketing automation" --max-hits 30
Empty --query "" returns the full index in the order Qwoted shows it
on the homepage (newest first).
Read the resulting JSON file (path is in the RESULT: line under
out_path). The structure is:
{
"query": "marketing automation",
"scraped_at": "2026-04-22T...",
"count": 30,
"opportunities": [
{
"source_request_id": 235897, // numeric ID for qwoted_pitch.py
"name": "Looking for marketing experts to comment on Q3 trends",
"details": "Reporter brief, 3-4 paragraphs of what they're after...",
"request_type": "Online article",
"deadline": "2026-04-28T17:00:00Z",
"want_pitches": true,
"publication": {"name": "TechCrunch", "top_publication": true, ...},
"hashtags": ["#marketing", "#saas"],
"url": "https://app.qwoted.com/source_requests/235897",
...
},
...
]
}
When the user asks for "the best", "top", or "easy wins", rank by:
publication.top_publication == true (high-DR sites)easy_win == true (Qwoted's signal: low pitch count, high responsiveness)paid == true (paid placements when applicable)pitch_count_category (lower is better — "low" beats "very_high")deadline_approaching, deadline)Only suggest opportunities where the user genuinely has expertise — journalists ignore obviously irrelevant pitches and Qwoted scores PR accounts on response rate.
After you've picked a shortlist, you MUST run each one through the Stage-3 heuristic table at the top of this file and present the classification to the user in a table. Do not skip this step, do not wait for the user to ask about stats pages. A typical output looks like this:
I'd prioritise 5 of the 8 opportunities. Here's the Stage-3 classification:
SR ID Publication Topic Deadline Stats-page score Action 235897 TechCrunch marketing automation trends 3d +5 Build marketing-automation-statistics-2026.html, then pitch236422 Selling Signals awareness vs demand 20h +1 Pitch direct 234782 BCA Insider local SEO for retailers 5d +4 Build local-seo-statistics-2026.html, then pitch236145 SEOptimer how agencies make money 8h -3 Pitch direct (too tight, founder-story) 232532 Business RoundUp founder interview 8d -3 Skip ($95 fee, founder-story) Plan: I'll build the 2 stats pages first (they cluster future opportunities too), then draft direct pitches for #235897, #236422 and #234782 that link to them. I'll draft a plain pitch for #236145 because of the deadline. Sound good?
When the user replies "ok" / "yes" / "go", move on to Stage 3 for the pages you identified, then Stage 4 for the pitches.
READ
STATISTICS_PAGE_PLAYBOOK.mdBEFORE EXECUTING THIS STAGE. This section is just the when. The how (research methodology, source quality bar, HTML build, anti-hallucination rules) lives in the playbook.
A pitch that just contains opinion gets one quote in one article. A
pitch that links to a comprehensive, sourced statistics page on the
same topic gets the user recurring citations for months — because
the next reporter who searches "<topic> statistics 2026" finds the
page on the user's domain and cites it without ever knowing the user
exists. This is the move that turns Qwoted from a one-off PR tool
into a compounding SEO engine.
Offer to build one when all of these are true:
Skip and go straight to Stage 4 when the deadline is tight or the ask is qualitative (anecdotes, opinions, case studies).
If multiple Stage 2 opportunities are on the same topic, build the
stats page once and reuse it across every pitch in that batch with
the same --research-page-url flag.
Don't just unilaterally start building a 3000-word page. Ask:
"Two of the opportunities I found are about [topic]. I can build you a sourced statistics page on this — it's a one-time effort that typically earns you backlinks for months because journalists cite it organically. The page would have ~50-80 stats, 2 charts, and a couple of comparison tables. Takes me ~5-10 minutes to research and draft. Want me to do it before pitching, or just pitch directly?"
STATISTICS_PAGE_PLAYBOOK.md — it has the full research
methodology, source quality bar, and HTML build instructions.python3 qwoted_profile.py --action get
Use first_pitchable_entity.name, .url, .company_name, .bio.templates/statistics_page_example.html../statistics_pages/<slug>.html.open ./statistics_pages/<slug>.html)
and ask them to publish on their own site (their CMS, their call).Each pitch should be:
Credit me as Jane Doe, founder of Acme Inc (acme.com)) and an offer to expand or
hop on a quick call.Save it to a tempfile so quoting is reliable:
cat > /tmp/qwoted_pitch.txt <<'EOF'
Hi! Borja Obeso here — founder of Distribb, a content distribution
and SEO platform that pushes one piece of content across 200+ DR40+
sites and channels. I see this national-vs-local split every day...
[2-4 paragraphs of substance]
Credit me as Borja Obeso, founder of Distribb (distribb.io).
— Borja
EOF
python3 qwoted_pitch.py \
--source-request-id 235897 \
--pitch-text-file /tmp/qwoted_pitch.txt
This creates a draft on Qwoted and autosaves the text. The reporter is NOT notified. Show the resulting draft to the user (or summarise it) and ask for approval.
python3 qwoted_pitch.py \
--source-request-id 235897 \
--pitch-text-file /tmp/qwoted_pitch.txt \
--research-page-url https://acme.com/blog/ai-marketing-statistics-2026 \
--send
The --research-page-url flag is optional but strongly recommended
when you built a Stage 3 stats page. It gets logged alongside the
pitch in ~/.qwoted/sent_pitches.json so you can later answer
questions like "which research pages drove the most pitches" or
"which page is the user citing in this PR push".
status: "sent" in the RESULT line means the reporter has been
notified. The pitch is also appended to ~/.qwoted/sent_pitches.json.
The script refuses to pitch a source-request that's already in
sent_pitches.json (returns status: "skipped_duplicate"). It also
detects pitches sent through the Qwoted UI (returns an error like
source_request_id=N already has a SENT pitch). To override locally,
pass --allow-duplicates (Qwoted itself only allows one pitch per
source-request, so the server-side block is hard).
RESULT.error contains... |
Meaning | Action |
|---|---|---|
No Qwoted session found / session expired |
Cookies missing or expired | Run python3 qwoted_login.py |
Cannot --send: no pitchable Source/Company/Product |
User skipped Stage 1c | Run qwoted_profile.py --action create ... |
already has a SENT pitch |
This SR has been pitched already | Pick a different opportunity |
is stuck in a non-draft, non-delivered state |
Previous submit attempt without entities | Pick a different opportunity (Qwoted won't let us re-edit) |
validation errors: ['Bio is too short'] |
Form-level issue | Adjust the field and re-run |
--send without showing the pitch to the user first. A
pitch is a real message to a real journalist — they remember spammers.STATISTICS_PAGE_PLAYBOOK.md.~/.qwoted/sent_pitches.json by hand — the
duplicate guard relies on it. Treat it as append-only state.~/.qwoted/ to git. It contains the user's session
cookies (storage_state.json) — full account access.https://<theirsite>/... — that's where the backlinks need to go.# Setup
python3 qwoted_login.py # idempotent: skips browser if session is still valid
python3 qwoted_login.py --force # open browser even if session is valid (switch accounts)
python3 qwoted_login.py --reset # wipe saved profile + cookie jar, start fresh
python3 qwoted_login.py --headless # headless only works if an existing session is valid
# Profile
python3 qwoted_profile.py --action get # what entities exist?
python3 qwoted_profile.py --action create --full-name '...' --bio '...' --email '...'
python3 qwoted_profile.py --action update --bio '...' # edit first Source
# Search
python3 qwoted_search.py --query "marketing automation" --max-hits 30
python3 qwoted_search.py --query "" --max-hits 50 # all opportunities
# Build a stats page (Stage 3)
# 1. READ STATISTICS_PAGE_PLAYBOOK.md for the research methodology
# 2. Open templates/statistics_page_example.html
# 3. Fill in placeholders → save to ./statistics_pages/<slug>.html
# 4. open ./statistics_pages/<slug>.html # preview
# 5. User publishes on their site → returns public URL
# Pitch
python3 qwoted_pitch.py --source-request-id 235897 --pitch-text-file /tmp/p.txt
python3 qwoted_pitch.py --source-request-id 235897 --pitch-text-file /tmp/p.txt --send
python3 qwoted_pitch.py --source-request-id 235897 --pitch-text-file /tmp/p.txt \
--research-page-url https://acme.com/blog/x-stats-2026 --send # with stats page
python3 qwoted_pitch.py --opportunity-id de1ccdba --pitch-text-file /tmp/p.txt --send # short URL form
State directory: ~/.qwoted/ (override with QWOTED_HOME env var).
AI-powered PR automation for Qwoted, with linkable-asset generation built in. Let your Claude Code agent set up your expert profile, find journalist requests, research and build a sourced statistics page on the topic (the kind journalists love to cite), and send pitches that link to it — earning press mentions and recurring high-DR backlinks while you focus on the work that matters.
Qwoted is the modern HARO replacement: every day, journalists at TechCrunch, Forbes, Inc., 1851 Franchise, Adweek and 6,000+ other publications post requests for expert sources on a topic they're writing about. Reply with a thoughtful pitch and you might land in the article — usually with a do-follow link back to your site (which is why this is one of the best white-hat SEO backlink channels in 2026).
The unique move in this skill is Stage 3: instead of just sending a naked opinion pitch, Claude can research and generate a comprehensive HTML statistics page on the topic (40-80 sourced stats, Chart.js charts, schema.org markup, ready to publish on your CMS). You publish it, Claude pitches with the link. The journalist gets quotable numbers; you get one-shot mentions plus recurring citations for months as other reporters discover your page through search.
This skill teaches Claude Code (or any AI agent that can shell out) how to drive Qwoted end-to-end on your behalf, so you can say things like:
…and have it actually happen.
npx skills add Bomx/qwoted-seo-backlinks-skill
Or clone manually:
git clone https://github.com/Bomx/qwoted-seo-backlinks-skill.git
cd qwoted-seo-backlinks-skill
pip install -r requirements.txt
playwright install chromium
That's it. Claude reads SKILL.md to learn the workflow.
Free account at qwoted.com. Pick the "I am a source / brand" option (not "I am media"). You'll get an email to verify.
Once the skill is installed, just say:
"Set me up on Qwoted. My name is Jane Doe, I'm the founder of Acme Inc, our website is acme.com and my LinkedIn is /in/jane-doe. We do marketing automation for B2B SaaS."
Claude will:
qwoted_login.py — a Chromium window opens; you sign in to
Qwoted (this includes MFA, captchas, anything Qwoted asks for).~/.qwoted/storage_state.json.qwoted_profile.py --action create ... to fill out your
"expert" Source persona (the thing Qwoted attaches to every pitch
so reporters know who's pitching them)."Find Qwoted opportunities about SaaS pricing this week, then pitch the top 3."
Claude will search the index, draft a custom pitch per opportunity
based on your bio, show each pitch to you for approval, and then
submit only the ones you say yes to. Sent pitches are logged to
~/.qwoted/sent_pitches.json so the same opportunity never gets
pitched twice.
| File | What it does |
|---|---|
qwoted_login.py |
One-time browser login. Saves cookies to ~/.qwoted/. Re-runs only if cookies expire (typically every 30 days). |
qwoted_profile.py |
Get / create / update your Qwoted "Source" persona — the bio, employer, location and contact links every pitch is attached to. |
qwoted_search.py |
Searches the same Algolia index Qwoted's website uses. Returns clean JSON with deadline, publication, request type, hashtags, etc. |
qwoted_pitch.py |
Drafts and (optionally) submits pitches via Qwoted's internal API. Defaults to dry-run; you must pass --send to actually fire. Optional --research-page-url flag logs the linked stats-page URL for traceability. |
STATISTICS_PAGE_PLAYBOOK.md |
The research methodology Claude follows when building a Stage-3 stats page: source quality bar, anti-hallucination rules, structure, length targets, anti-patterns. |
templates/statistics_page_example.html |
Self-contained HTML scaffold (modern responsive CSS, Chart.js charts, Article + FAQPage schema markup, print-friendly). Claude fills in the placeholders. |
SKILL.md |
The Claude Code playbook itself — frontmatter + 4-stage workflow. |
All four scripts are pure Python (requests + playwright) and have
an opinionated RESULT: { ... } JSON output line so AI agents can
chain them together reliably.
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ 1. Onboard │ → │ 2. Find │ → │ 3. Research + │ → │ 4. Pitch │
│ (login + │ │ opportunity │ │ publish a stats │ │ with the │
│ profile) │ │ │ │ page (linkable │ │ page URL │
│ │ │ │ │ asset) │ │ │
└──────────────┘ └──────────────┘ └──────────────────┘ └──────────────┘
once every session once per topic every pitch
Stage 3 is the multiplier. A naked pitch lands one quote in one
article. A pitch that links to a thoroughly-sourced stats page lands
recurring citations for months because the next reporter who
searches "<topic> statistics 2026" finds the page on your domain
and cites it on their own.
Everything lives under ~/.qwoted/ and ./statistics_pages/:
~/.qwoted/
├── storage_state.json # session cookies (full account access — do NOT share)
├── chromium-profile/ # persistent Chromium profile so re-logins are 1-click
├── profile_state.json # snapshot of your Sources/Products/Companies
├── sent_pitches.json # append-only log w/ research_page_url tracking
└── opportunities/ # JSON dumps from each search
└── marketing_20260422_153058.json
./statistics_pages/ # generated stats pages (Stage 3 output)
└── ai-in-marketing-statistics-2026.html
Override the ~/.qwoted/ location with QWOTED_HOME=/some/path.
Reset by deleting the folder.
You can use this skill as a regular CLI too:
# Set up
python3 qwoted_login.py
python3 qwoted_profile.py --action get
# Find opportunities about SaaS pricing
python3 qwoted_search.py --query "saas pricing" --max-hits 20
# Dry-run a pitch (no email sent)
python3 qwoted_pitch.py \
--source-request-id 235897 \
--pitch-text-file ./my_pitch.txt
# Actually send it
python3 qwoted_pitch.py \
--source-request-id 235897 \
--pitch-text-file ./my_pitch.txt \
--send
Run any script with --help for the full flag list.
Qwoted's pitch API will happily accept a submission without a
pitchable entity attached (HTTP 200, draft=false), but the reporter
is never notified. The pitch is functionally dead. We learned this
the hard way; the skill now refuses to fire --send if no Source /
Product is attached, and the setup wizard makes sure you have one
before you can pitch.
~/.qwoted/storage_state.json never leaves
your machine. It IS effectively your Qwoted password — .gitignore
blocks it from being committed accidentally.| Symptom | Fix |
|---|---|
No Qwoted session found |
python3 qwoted_login.py |
| Login window opens but never closes | You're stuck on Qwoted's login page. Sign in fully. |
Cannot --send: no pitchable Source/Company/Product |
Run qwoted_profile.py --action create ... first. |
already has a SENT pitch |
Qwoted only allows one pitch per source-request. Pick a different one. |
Playwright says Executable doesn't exist |
Run playwright install chromium once. |
This skill is one piece of a bigger system. Distribb handles the rest:
→ Automate your SEO and backlinks at distribb.io
Built by Borja Obeso, founder of Distribb. Related: the Distribb Claude skill for keyword research, backlink exchange and CMS publishing.
qwoted automation, qwoted api, qwoted skill claude, claude code seo skill, auto pr backlinks, journalist pitch automation, haro alternative automation, pr outreach ai agent, expert source pr tool, automated press mentions, ai-generated statistics page,
linkable asset for seo, journalist citation backlinks,
programmatic seo pr, claude code stats page generator,
haro pitch automation 2026.