browser-backlink-submission

Skill

Operational playbook for executing backlink submissions via a headless browser: driving directory, listing, and business-profile forms with Playwright, mapping a single site profile to per-target fields, handling signup + email verification + captchas, human-paced throttling within Google's link-spam policies, and producing an auditable submission log. Activate only when the user has a qualified prospect list and wants the browser to submit. Prospect discovery, qualification, and anchor/description copy are upstream concerns; this skill is execution-only.

Apache-2.0 Requires browser:general agent with browser (Playwright) capability and filesystem-write. Assumes a prospect list already qualified for safety and topical fit.

Instructions

Overview

Given a qualified prospect list and a single source-of-truth site profile, drive each target's submission form to completion, capture evidence, and emit an auditable log. This skill is execution-only — it assumes prospects are already vetted upstream and assumes anchor / description copy is either pre-supplied or fetched via the agent's main loop.

Outcome: a submission-log.json with one row per attempt (status, evidence, live URL once verified) plus screenshots under ./out/backlinks/. No fabricated successes, no fire-and-forget, no link-farm targets ever.

Mental model

A submission run is a pipeline: profile → per-target field mapping → form drive → evidence capture → verification follow-up. Most failures are not bugs — they are signals: captcha walls, login-required, paid-only, dead form, off-topic category. The skill's job is to detect, classify, and log these honestly so the user gets a reliable picture of what shipped.

Set expectations from Google's spam policies: "low-quality directory or bookmark site links", "using automated programs or services to create links to your site", and optimized anchor text in distributed content are all listed as link spam, and most directories mark outbound links nofollow or ugc anyway. A defensible run is a short list of human-reviewed listings with a real audience — startup and SaaS directories, review sites, business profiles, industry associations — whose value is referral traffic, discovery, and consistent business citations, not PageRank. Submit at human pace, one listing per target, as a person would.

Three immovable invariants:

  1. Never submit to a target the prospect list did not qualify. If a target looks shady mid-run (hidden links, casino/pharma neighbours, "1000 directories for $5" footer), skip and log skipped:quality.
  2. One identity per run. Reuse the same email, brand name, contact, anchor pool — directories that detect inconsistency flag listings. The profile is the source of truth.
  3. Evidence or it didn't happen. Every submitted row needs a post-submit screenshot and either a confirmation URL or a confirmation-email subject line.

Rules

The site profile (input contract)

The user (or an upstream step) supplies one JSON file. If any required field is missing, stop and ask — do not invent values.

FieldRequiredNotes
nameyesBrand / site / product name as it should appear
urlyesCanonical URL, no UTM, https
taglineyes≤ 80 chars
description_shortyes≤ 160 chars
description_longyes300–600 chars, neutral tone
description_altrecommendedA second long variant for forms where the first does not fit the field or the category
category_primaryyesMap to each directory's taxonomy
category_altrecommendedFallback when primary doesn't exist on a target
tagsyes5–10 keywords, lowercase
logo_pathyes if any target requires imageLocal file; PNG ≥ 256 px square preferred
screenshot_pathrecommendedFor directories asking for a product preview
contact_emailyesReal, monitored — verification mail lands here
contact_nameyesReal human; some directories reject "Admin"
pricingrecommended"free" / "freemium" / "paid" — startup directories ask
founded_year, location, team_sizerecommendedBusiness directories ask
social.twitter, social.linkedin, social.githubrecommendedMany forms require ≥ 1 social URL
anchorsyes1–3 entries: the brand name and the naked URL (optionally brand + product type). Keyword-rich anchors are not used.

Target classification (per prospect, before driving the form)

Target typeTreat asNotes
Free general directory (curated, with editorial review and real traffic)submitVerify it's still live, indexed, and reviewed by humans
Business profile (Google Business Profile, Bing Places, Apple Business Connect, Yelp, BBB)submit, ownership-verifyOften needs phone, postcard, or video verification — log as pending:ownership-verify; name, address, and phone must match the profile exactly
Startup / SaaS directory (BetaList, F6S, AlternativeTo, SaaSHub, Tools Directory style)submitUsually needs account + email verify
Web2.0 property (Medium, Tumblr, WordPress.com, Notion public pages)skipThin posts created to carry a link are link spam under Google's policies — log skipped:not-a-form
Niche industry list (curated GitHub awesome lists, vertical aggregators)PR / issue, not formReroute: open issue / PR — out of this skill's scope, log skipped:not-a-form
Q&A / forum (Quora, Reddit, Stack Exchange)NOT this skillPersona / on-topic answers are out of scope — log skipped:not-a-form
Paid-only ($/listing)skip unless user pre-approved budgetLog skipped:paid with the price
Link farm / "submit to 500 directories" / spammy ad-laden / hidden links / unrelated nicheskip, hardLog skipped:quality with the reason
Captcha-walled (hCaptcha, reCAPTCHA, Cloudflare Turnstile, "verify you are human" pages)stop and escalateDo not attempt to bypass. Invisible score-based checks can reject a submit silently — a form that resets or errors with no message after submit counts here too. Log skipped:captcha; the user solves manually if they want
Login-required and we have no accountescalateEither user supplies creds, or log skipped:auth

Field-mapping rules

  • name → "Site Name", "Title", "Project", "Tool name", "Company". Match what the form asks; never invent prefixes/suffixes.
  • url → "URL", "Website", "Homepage". Always canonical https; never with tracking params.
  • description_short → "Tagline", "Short description", "Pitch", fields with ≤ 160 char limit.
  • description_long / description_alt → "Description", "About", "Details" (300+ chars). Pick the variant that fits the field and the category.
  • category → match to directory taxonomy by exact label first, then nearest synonym. If nothing fits, pick the closest and log category_mapping.
  • tags → split into the form's expected separator (comma, space, enter-per-tag). Trim to the form's max count.
  • anchors → only used when the target lets you choose anchor text on the link. Use the brand name or the naked URL.
  • logo / screenshot → upload with browser_file_upload (absolute paths) after clicking the file input; verify the upload preview appears before submit.

Drive protocol (per target)

  1. Pre-flight — open URL, confirm it's the submission form (not 404, not paywall, not "submissions closed"). If wrong, log and move on.
  2. Account check — if the form requires an account: look up cached creds for that domain in this session's profile vault; if absent, ask user once, then proceed. Never store creds to disk.
  3. Captcha check — if a captcha is on the submit page, stop. Do not iterate.
  4. Field walk — accessibility-tree first; fill in the order the form lays them out; verify each value sticks (re-read the input).
  5. Category mapping — if a select / radio is required, pick by label match; if no match, pick the nearest sibling and record the mapping in the log row.
  6. Upload check — for image fields, confirm the preview thumbnail rendered before submit.
  7. Submit — click the actual submit button; wait for a success state: redirect, success message, confirmation page selector, or verification email.
  8. Evidence — screenshot the post-submit state (full page) to ./out/backlinks/<domain>-<UTC>.png; capture the confirmation URL or message text.
  9. Verification — if the target sends a confirmation email, poll the user's mailbox via the supplied flow (out of scope of this skill — escalate if no flow available); record the verified URL when it goes live.
  10. Log — append one row to ./out/backlinks/submission-log.json.

Throttling and identity hygiene

  • Spread across time: never submit to more than 5 targets in a row without a 60-second cool-off.
  • Pace per day: cap free-directory submissions at 20 per day per identity; business profiles at 5.
  • One profile, one email, one contact name across the entire run. Inconsistent business details split citations and get listings rejected.
  • Read each target's submission rules first; skip targets whose terms forbid automated submission or require a reciprocal link (log skipped:quality).

Submission log schema

./out/backlinks/submission-log.json is an array of rows. Append-only.

text
{
  "target_url": "https://example-directory.com/submit",
  "target_domain": "example-directory.com",
  "target_type": "saas-directory",
  "submitted_at": "2026-05-10T18:32:11Z",
  "status": "submitted | pending:email | pending:ownership-verify | live | rejected | skipped:captcha | skipped:paid | skipped:quality | skipped:auth | skipped:not-a-form | failed:selector | failed:timeout",
  "anchor_used": "the brand name",
  "description_variant": "long | alt",
  "category_mapping": { "requested": "Developer Tools", "selected": "Tools / Dev" },
  "live_url": null,
  "screenshot_path": "./out/backlinks/example-directory-com-20260510T183211Z.png",
  "confirmation_url": null,
  "confirmation_email_subject": null,
  "notes": "Selector for category was a custom dropdown; mapped Tools → Tools / Dev."
}

Examples

Example 1: Bad → Good (anchor handling)

❌ Bad:

text
For all 50 directories, rotate keyword anchors ("best project management tool", "PM software") to look natural.

✅ Good:

text
Where anchor text is selectable, use the brand name or naked URL. Submit only to the 12 directories with real traffic and editorial review.

What changed: engineered keyword anchors across directories are the link-spam pattern Google's policies name; brand anchors on a few real listings are what a business would do anyway.

Example 2: Edge case — category not in taxonomy

The site is a Developer Tools SaaS but the directory only offers Software / Other, Business / Productivity, Lifestyle. Pick Software / Other, record category_mapping: { requested: "Developer Tools", selected: "Software / Other" }, do not pick Business / Productivity because it's slightly broader — closer is better than larger.

Example 3: Edge case — captcha on submit

Form filled cleanly, "Submit" button reveals an hCaptcha challenge. Stop. Screenshot. Log skipped:captcha. Do not refresh and retry; do not attempt an OCR/solver. Move to the next target.

Checklist

  • Site profile JSON validated — all required fields present, anchors[] has ≥ 3 entries, logo file exists if any target needs it
  • Prospect list classified — each target has a target_type; link-farm / unrelated targets pre-filtered out
  • Output directory ./out/backlinks/ exists; previous submission-log.json either rotated or appended to deliberately
  • Per submission: post-submit screenshot saved, log row appended, status reflects reality (no "submitted" without evidence)
  • Anchors are brand name or naked URL only; descriptions chosen to fit each field
  • Throttling respected — pace caps and cool-offs honored
  • Captcha / paid / login-required targets skipped, not faked
  • No credentials persisted to disk after the run
  • Final report summarizes: total attempted, submitted, pending verification, skipped (by reason), failed (by reason)

Composition / References