{
  "openapi": "3.0.1",
  "info": {
    "title": "Link Building Prospects & Guest Post Finder (Google, AI)",
    "description": "First 25 prospects free. Link building prospects from Google, pay per qualified prospect: guest-post, resource and listicle pages with contact route, fit score and AI opener. Free demo on any plan; live search runs on your Apify account, any plan that runs Store Actors.",
    "version": "0.3",
    "x-build-id": "OjS1tFtRwryRq4TdY"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/rich_minds~link-building-prospects-ai/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-rich_minds-link-building-prospects-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/rich_minds~link-building-prospects-ai/runs": {
      "post": {
        "operationId": "runs-sync-rich_minds-link-building-prospects-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/rich_minds~link-building-prospects-ai/run-sync": {
      "post": {
        "operationId": "run-sync-rich_minds-link-building-prospects-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "keywords": {
            "title": "Keywords",
            "type": "array",
            "description": "1–10 topics your target page is about; each becomes the footprint searches for the types you pick (e.g. <code>\"remote work\" \"write for us\"</code>). 4 keywords × 3 types = 24 Google searches ≈ $0.11 — affects what you pay for the search. Empty = read from <b>Page you want links to</b>. Typing your own next to the sample starts the live search: it runs a Store Actor on your Apify plan (any plan that runs Store Actors); if yours cannot, paste rows into <code>itemsList</code> (list mode, any plan).",
            "items": {
              "type": "string"
            }
          },
          "targetUrl": {
            "title": "Page you want links to",
            "type": "string",
            "description": "The article, tool or page you are building links for — the fit score and the opener use it. With <b>Keywords</b> empty, the live search reads its keywords from this page: one URL is enough to start. The live search runs on your Apify plan (any plan that runs Store Actors)."
          },
          "senderPitch": {
            "title": "What you offer site owners (your pitch)",
            "type": "string",
            "description": "One line on what you bring — a guest article idea, a free tool, a guide worth listing. The opener and the pitch angle build on it, never on invented claims."
          },
          "preset": {
            "title": "Start from a ready niche search (one click)",
            "enum": [
              "custom",
              "saas-guest-posts",
              "digital-pr",
              "agency-roster"
            ],
            "type": "string",
            "description": "Pick a niche and press Start: it fills the search fields you left empty and runs the live search (spend-capped by <code>maxDiscoveryChargeUsd</code>). Your own values always win. <b>Custom</b> = fill the fields yourself.",
            "default": "custom"
          },
          "minScore": {
            "title": "Minimum fit score (0–100)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "The field that decides what you pay: prospects below this fit score are discarded and <b>not charged</b>. 70+ = pitch this week, 45–69 = maybe.",
            "default": 60
          },
          "maxQualified": {
            "title": "Max qualified prospects to output",
            "minimum": 1,
            "maximum": 10000,
            "type": "integer",
            "description": "Hard cap on results — and the ceiling of what <b>this Actor</b> charges: at most this many × $0.018 with AI on (× $0.006 with AI off), e.g. 50 × $0.018 = $0.90; the Google search has its own cap right below. Affects what you pay. Best-scoring prospects first; per client in agency mode.",
            "default": 50
          },
          "maxDiscoveryChargeUsd": {
            "title": "Max Google search spend (USD)",
            "minimum": 0.01,
            "maximum": 1000,
            "type": "number",
            "description": "Hard cap on what the Google search (<code>apify/google-search-scraper</code>) may charge your account: queries that would cost more are dropped before the search starts. $0.50 in the form and for an API call that leaves it out. Affects what you pay.",
            "default": 0.5
          },
          "enableAi": {
            "title": "AI assessment",
            "type": "boolean",
            "description": "<b>On = $0.018 per qualified prospect + AI tokens; off = $0.006</b>. The AI adds the opportunity type and a 0–100 fit score with quotes from the page, an opener and a pitch angle that cite the page, and the site's submission rules. Your first 5 prospects get live AI verdicts on any plan; every one with Apify model access or a paid own key.",
            "default": true
          },
          "notifyEmail": {
            "title": "E-mail me the new prospects (optional)",
            "type": "string",
            "description": "After every run with at least one new prospect, the digest (how many are new, the top 10 with their contact route) is e-mailed here. Schedule the run weekly and only new prospects come to your inbox. Never sent for the free demo."
          },
          "opportunityTypes": {
            "title": "Opportunity types",
            "type": "array",
            "description": "Which link opportunities to search for and deliver. Each type adds its own footprint searches; a prospect of a type you did not pick is dropped free. <b>Unlinked mentions</b> need <b>Your brand name</b>; <b>Competitor mentions</b> need <b>Competitors</b> (naming competitors switches it on).",
            "items": {
              "type": "string",
              "enum": [
                "guest_post",
                "resource_page",
                "listicle",
                "review",
                "forum",
                "news",
                "unlinked_mention",
                "competitor_mention",
                "other",
                "any"
              ],
              "enumTitles": [
                "Guest posts (write for us)",
                "Resource / links pages",
                "Listicles (best X tools, top 10)",
                "Reviews",
                "Forums & communities",
                "News articles",
                "Unlinked brand mentions",
                "Competitor mentions (they cover a competitor, not you)",
                "Other pages",
                "Any type"
              ]
            },
            "default": [
              "guest_post",
              "resource_page",
              "listicle"
            ]
          },
          "minAuthority": {
            "title": "Minimum domain authority (0–100)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Drop sites below this <code>authorityScore</code> — free, before the AI. The score is a log scale of the site's Tranco rank (top 1 000 ≈ 54, top 100 000 ≈ 23), or Open PageRank × 10 with your key. Sites outside Tranco's top 1M have no score and are dropped by any value above 0. 0 = keep every site.",
            "default": 0
          },
          "requireContactRoute": {
            "title": "Only prospects with an e-mail or contact page",
            "type": "boolean",
            "description": "On = a prospect without a public e-mail or a contact page is dropped free. Off = it is delivered with <code>hasContactRoute: false</code>.",
            "default": false
          },
          "countryCode": {
            "title": "Google country",
            "type": "string",
            "description": "Two-letter country of the Google search (us, gb, de, in, au …) — prospects that rank where your buyers are.",
            "default": "us"
          },
          "ownDomains": {
            "title": "Your own domains",
            "type": "array",
            "description": "Your sites (and their subdomains) — never delivered as prospects. Also used to tell whether a page already links to you.",
            "items": {
              "type": "string"
            }
          },
          "ignoredDomains": {
            "title": "Domains to ignore",
            "type": "array",
            "description": "Competitors, sites you never pitch, sites you pitched outside this Actor. Dropped free, subdomains included.",
            "items": {
              "type": "string"
            }
          },
          "brandName": {
            "title": "Your brand name",
            "type": "string",
            "description": "Checked on every page (title, snippet, text). With <b>Unlinked brand mentions</b> picked, pages that name you without linking become prospects; otherwise see the next field.",
            "default": ""
          },
          "excludeBrandMentioned": {
            "title": "Drop pages that already mention your brand",
            "type": "boolean",
            "description": "On = a page that already names your brand is dropped free (you probably know it). Ignored when you pick <b>Unlinked brand mentions</b>.",
            "default": true
          },
          "competitorNames": {
            "title": "Competitors (digital PR)",
            "type": "array",
            "description": "Brands or products you compete with. Each one adds a Google search for pages that name it but not <b>Your brand name</b> (<code>\"Competitor\" -\"YourBrand\"</code>); pages that mention a competitor and not you become <code>competitor_mention</code> prospects with the competitor in <code>competitorsMentioned</code> — the sites to ask for a spot next to them.",
            "items": {
              "type": "string"
            }
          },
          "targetFlags": {
            "title": "Target flags (any of)",
            "type": "array",
            "description": "Only deliver prospects that carry at least one of these flags (e.g. <b>has_email</b>, <b>accepts_guest_posts</b>, <b>has_broken_link</b>). Empty = no flag filter.",
            "items": {
              "type": "string",
              "enum": [
                "has_email",
                "has_contact_page",
                "has_guidelines",
                "accepts_guest_posts",
                "sponsored_mentioned",
                "nofollow_heavy",
                "brand_mentioned",
                "competitor_mentioned",
                "has_broken_link",
                "high_authority",
                "personal_email",
                "email_mx_ok",
                "keyword_in_title",
                "top_3",
                "recent",
                "wordpress",
                "no_contact_route",
                "fetch_failed"
              ],
              "enumTitles": [
                "has_email — a public e-mail address was found on the site",
                "has_contact_page — a contact page is linked from the page",
                "has_guidelines — a write-for-us / contributor guidelines page was found",
                "accepts_guest_posts — the site says it accepts guest posts",
                "sponsored_mentioned — the site mentions sponsored or paid posts",
                "nofollow_heavy — half or more of the page's outbound links are nofollow",
                "brand_mentioned — your brand is already named on the page (unlinked mention)",
                "competitor_mentioned — one of your `competitorNames` is named on the page (and your brand is not)",
                "has_broken_link — the resource page / listicle links to a dead page (404 / 410 / domain gone) — pitch the fix",
                "high_authority — authorityScore 40 or more (≈ Tranco top 8 000, or Open PageRank 4+)",
                "personal_email — the best e-mail belongs to a named person, not a shared inbox",
                "email_mx_ok — the best e-mail's domain has a mail server (MX check)",
                "keyword_in_title — your keyword is in the page title",
                "top_3 — ranks in Google's top 3 for its footprint query",
                "recent — published or updated in the last 12 months",
                "wordpress — the site runs on WordPress",
                "no_contact_route — no e-mail or contact page was found",
                "fetch_failed — the page could not be fetched — scored on Google data only"
              ]
            }
          },
          "suppressionList": {
            "title": "Exclude list",
            "type": "array",
            "description": "Domains or URLs to never output. Suppressed prospects are skipped before any processing and never charged.",
            "items": {
              "type": "string"
            }
          },
          "findContactRoute": {
            "title": "Find the contact route on each site",
            "type": "boolean",
            "description": "Free: reads the prospect page and one of its contact / write-for-us pages (robots.txt honoured, max 2 pages per site) for public e-mails, the contact page, the guidelines page and social profiles. Off = faster, Google data only.",
            "default": true
          },
          "checkEmails": {
            "title": "Check the e-mails and pick the best one",
            "type": "boolean",
            "description": "Free: checks that each e-mail's domain has a mail server (MX), marks shared inboxes (info@, contact@ …) and picks <code>bestEmail</code> — a named person first, then an editorial inbox — with <code>emailStatus</code>, <code>isRoleAddress</code> and <code>firstName</code> for Lemlist / Instantly. No mailbox (SMTP) verification.",
            "default": true
          },
          "checkBrokenLinks": {
            "title": "Find dead links on resource pages and listicles",
            "type": "boolean",
            "description": "Free: checks the outbound links of every resource page / listicle prospect (HEAD request, max 15 per page). A link that answers 404 / 410 or whose domain is gone lands in <code>brokenLinks</code>, sets the <code>has_broken_link</code> flag and turns the opener into the broken-link pitch: \"the link to X on your page no longer works — my page could replace it\".",
            "default": true
          },
          "lookupAuthority": {
            "title": "Look up domain authority",
            "type": "boolean",
            "description": "Free: <code>authorityScore</code> (0–100) and <code>authorityRank</code> per site from the Tranco top-1M ranking (one download per run, ≈ 2 s), or from Open PageRank when you add a key below. Off = no authority figures and <b>Minimum domain authority</b> is ignored.",
            "default": true
          },
          "openPageRankApiKey": {
            "title": "Open PageRank API key (optional)",
            "type": "string",
            "description": "A free key from domcop.com/openpagerank adds a 0–10 PageRank for small sites too (Tranco only ranks the top 1M). Stored encrypted by Apify, never logged."
          },
          "writeSequence": {
            "title": "AI writes the follow-up and break-up e-mails too",
            "type": "boolean",
            "description": "AI on only. Adds <code>followUp</code> (≤ 60 words, one new reason to reply) and <code>breakUp</code> (≤ 40 words) to every hot / warm prospect, grounded in the same page — a 3-step sequence ready for Lemlist / Instantly. Same event price; only a few more AI tokens.",
            "default": false
          },
          "senderName": {
            "title": "Your name (optional)",
            "type": "string",
            "description": "Only used so the AI never invents a sender name — leave empty and the opener has none.",
            "default": ""
          },
          "llmProvider": {
            "title": "AI model access",
            "enum": [
              "apify",
              "byok"
            ],
            "type": "string",
            "description": "<b>Apify (no keys)</b> — the AI runs through Apify's built-in OpenRouter proxy; tokens are billed to your Apify account at OpenRouter's rates. <b>My own key</b> — your OpenAI / Anthropic / Gemini / Groq key instead (empty <b>AI model</b> = Gemini: a free key from aistudio.google.com/apikey works); a free-tier key assesses only a few prospects per run — expect 1–3 AI checks — and the rest keep their rule score; a paid Groq / Gemini key (billing on) assesses every prospect.",
            "default": "apify"
          },
          "llmModel": {
            "title": "AI model",
            "type": "string",
            "description": "Leave empty for the default: <code>anthropic/claude-haiku-4.5</code> with Apify model access, <code>google:gemini-3.5-flash-lite</code> with your own key (a free Gemini key works). Apify mode takes an OpenRouter slug such as <code>openai/gpt-4.1-mini</code>; own-key mode takes <code>provider:model</code>, e.g. <code>groq:openai/gpt-oss-120b</code>."
          },
          "llmApiKey": {
            "title": "Your API key (own-key mode only)",
            "type": "string",
            "description": "Required when <b>AI model access</b> is <i>My own API key</i>. Stored encrypted by Apify, never logged."
          },
          "aiCandidateMultiplier": {
            "title": "AI candidate pool (× max qualified)",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "How many of the best rule-scored prospects get the AI pass, as a multiple of <b>Max qualified prospects</b>. Higher = more thorough, slower, more tokens.",
            "default": 2
          },
          "sendAlertsTo": {
            "title": "Send alerts to (Slack, Discord, Teams, Google Chat, Telegram or webhook)",
            "type": "string",
            "description": "Paste one or more URLs, one per line — the service is detected from the address: a Slack or Discord channel webhook, a Teams Workflows or Google Chat space webhook, your Telegram bot's send link (<code>https://api.telegram.org/bot&lt;token&gt;/sendMessage?chat_id=@yourchannel</code>) or any other https URL (Zapier, Make, n8n, your CRM), which gets the prospects as JSON. After every run with at least one new prospect, the digest (how many are new, the top 10 with links) is posted there. Never sent for the free demo. Headers, batching and signing: <i>Delivery details (advanced)</i>."
          },
          "lemlistApiKey": {
            "title": "Lemlist API key",
            "type": "string",
            "description": "Adds every new prospect with a <code>bestEmail</code> to your Lemlist campaign after the run pushes it (firstName, companyName, website, the opener as <code>icebreaker</code>, the page, pitch angle and dead link as custom variables). Lemlist → Settings → Integrations → API. Stored encrypted by Apify, never logged."
          },
          "lemlistCampaignId": {
            "title": "Lemlist campaign ID",
            "type": "string",
            "description": "The campaign the prospects are added to (<code>cam_…</code> from the campaign's URL). Duplicates are skipped by Lemlist."
          },
          "instantlyApiKey": {
            "title": "Instantly API key (v2)",
            "type": "string",
            "description": "Adds every new prospect with a <code>bestEmail</code> to your Instantly campaign (email, first_name, company_name, website, the opener as <code>personalization</code>, custom variables). Instantly → Settings → Integrations → API keys. Stored encrypted, never logged."
          },
          "instantlyCampaignId": {
            "title": "Instantly campaign ID",
            "type": "string",
            "description": "The campaign the prospects are added to (the ID in the campaign's URL). Leads already in your workspace are skipped."
          },
          "dedupeAcrossRuns": {
            "title": "Never output the same domain twice",
            "type": "boolean",
            "description": "Remembers every domain you received (in a named key-value store on your account) and skips it — free — in future runs, whichever of its pages ranks next time.",
            "default": true
          },
          "dedupeTtlDays": {
            "title": "Deliver a prospect again after (days)",
            "minimum": 0,
            "maximum": 3650,
            "type": "integer",
            "description": "How long a delivered site stays remembered. 0 (default) = a site you received is never delivered or charged again. A number of days lets a site come back after that many days — e.g. 365 to re-pitch yearly — charged again as a new prospect.",
            "default": 0
          },
          "historyDatasetName": {
            "title": "Keep a running log (dataset name)",
            "type": "string",
            "description": "Optional. Type a name (e.g. <code>link-building-prospects-ai-seen-log</code>) and every run appends its new prospects with the date to that named dataset on your account — one list across months to connect to Google Sheets or your BI tool once. Empty = off. Costs nothing extra.",
            "default": ""
          },
          "clients": {
            "title": "Clients (agency mode)",
            "type": "array",
            "description": "One run — one schedule — for all your clients (the two entries shown are an example: replace them, or pick the <b>SEO agency</b> preset to run them). Each entry is one client's brief: <code>name</code> plus any of <code>keywords</code>, <code>targetUrl</code>, <code>senderPitch</code>, <code>ownDomains</code>, <code>ignoredDomains</code>, <code>brandName</code>, <code>competitorNames</code>, <code>suppressionList</code>, <code>notifyEmail</code>, <code>lemlistCampaignId</code>, <code>instantlyCampaignId</code>. Every other setting is shared. Each client gets its own search, its own never-twice memory, a <code>client</code> column (view <i>By client</i>), its own digest e-mail and campaign. Max 25 clients per run; <code>maxQualified</code> and <code>maxDiscoveryChargeUsd</code> apply per client."
          },
          "sourceMode": {
            "title": "Where do the prospects come from?",
            "enum": [
              "actor",
              "dataset",
              "list"
            ],
            "type": "string",
            "description": "<b>actor</b> — search Google live: the footprint queries built from your keywords run with <code>apify/google-search-scraper</code> on your Apify account ($0.0045 per Google page + $0.001 per run, billed by that Actor and capped by <b>Max Google search spend</b>). <b>dataset</b> — an existing Google Search Results Scraper dataset (any plan, no search cost). <b>list</b> — paste Google results or plain URLs, e.g. an Ahrefs / Semrush export (any plan). <b>Bring your own data:</b> rows or a dataset ID, and you pay only for the analysis — the source Actor is never run. The mode you choose always runs as chosen; only the untouched sample records next to keywords or a page you typed switch to the live search (spend-capped). <code>{}</code> over the API runs the free demo — nothing charged.",
            "default": "actor"
          },
          "autoSwitchToLive": {
            "title": "Typed search next to the sample starts the live search",
            "type": "boolean",
            "description": "On (default): search terms typed next to the untouched sample records start the live search (spend-capped; <code>OUTPUT.switchedToLive</code> names them). Off: the mode you send is the mode that runs — the sample stays the free demo and the status names the terms it did not search. API pipelines that must never start a paid read on their own: send <code>false</code>.",
            "default": true
          },
          "datasetId": {
            "title": "Dataset",
            "type": "string",
            "description": "Only for <b>dataset</b> mode. A Google Search Results Scraper dataset (one item per Google page with <code>organicResults</code>, or its one-result-per-row view). Pick the dataset so the Actor is granted read access to it."
          },
          "itemsList": {
            "title": "Google results or URLs to process",
            "type": "array",
            "description": "Only for <b>list</b> mode. JSON array of URLs, or of objects with <code>url</code> and optionally <code>title</code>, <code>description</code>, <code>position</code>, <code>query</code> — e.g. an Ahrefs / Semrush / SERP export. The template with the field names: README → List-mode rows."
          },
          "searchLanguage": {
            "title": "Google language",
            "type": "string",
            "description": "Two-letter language of the results (en, de, fr, es …).",
            "default": "en"
          },
          "maxPagesPerQuery": {
            "title": "Google pages per footprint query",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "1 page = the top ~10 results of each footprint search ($0.0045 per page on your account). 2–3 digs deeper for niche keywords.",
            "default": 1
          },
          "quickDateRange": {
            "title": "Only pages from the last …",
            "enum": [
              "any",
              "d7",
              "m1",
              "m6",
              "y1",
              "y2"
            ],
            "type": "string",
            "description": "Google's date filter — fresher pages, active sites.",
            "default": "any"
          },
          "footprints": {
            "title": "Custom footprints",
            "type": "array",
            "description": "Your own operator searches, one per line; <code>{keyword}</code> is replaced by each keyword, e.g. <code>\"{keyword}\" inurl:write-for-us</code> or <code>\"{keyword}\" \"guest contributor\"</code>. They run before the built-in ones.",
            "items": {
              "type": "string"
            }
          },
          "maxQueries": {
            "title": "Max footprint queries per run",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Caps the number of Google searches (keywords × footprints). The cheapest control of your search spend.",
            "default": 40
          },
          "discoveryActorId": {
            "title": "Source Actor (advanced)",
            "type": "string",
            "description": "Actor used in <b>actor</b> mode. Any Actor whose output has the Google Search Results Scraper fields (<code>organicResults</code> or one result per row) works.",
            "default": "apify/google-search-scraper"
          },
          "discoveryInput": {
            "title": "Extra source Actor input (advanced)",
            "type": "object",
            "description": "Merged into the Google Search Results Scraper input and wins over the Actor's own values — e.g. <code>{\"websiteContentScraper\": true}</code> to have the source scrape each page (then nothing is fetched here), or your own <code>queries</code> instead of the footprints."
          },
          "maxToProcess": {
            "title": "Max candidates to process",
            "minimum": 1,
            "maximum": 20000,
            "type": "integer",
            "description": "Upper bound on how many Google results are checked in one run (controls run time). Default = 3 × max qualified."
          },
          "proxyConfiguration": {
            "title": "Proxy for the contact-route fetch",
            "type": "object",
            "description": "Only for reading the prospects' own pages. Off by default — blogs rarely block a single polite request; switch Apify Proxy on if many fetches fail."
          },
          "webhookUrl": {
            "title": "Webhook URL (optional)",
            "type": "string",
            "description": "Qualified prospects are POSTed here as JSON (Zapier, Make, n8n, your CRM or outreach tool). Stored encrypted — Zapier / Make hook URLs carry their secret in the path."
          },
          "webhookHeaders": {
            "title": "Webhook headers (optional)",
            "type": "object",
            "description": "Extra HTTP headers for the webhook, e.g. <code>{\"Authorization\": \"Bearer …\"}</code>."
          },
          "webhookBatchSize": {
            "title": "Webhook batch size",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "1 = one POST per prospect the moment it is ready. Higher = one POST per N prospects.",
            "default": 1
          },
          "webhookSecret": {
            "title": "Webhook signing secret (optional)",
            "type": "string",
            "description": "Any random string. Every webhook POST then carries <code>X-Signature: sha256=&lt;HMAC-SHA256 of the raw body with this secret&gt;</code>, so your endpoint can verify it came from this Actor. Stored encrypted by Apify, never logged."
          },
          "dedupeStoreName": {
            "title": "Dedupe store name",
            "type": "string",
            "description": "Key-value store used for cross-run memory. Left at the default, the memory is kept per client: a different <code>targetUrl</code> / <code>ownDomains</code> / <code>brandName</code> starts its own list, so one client never hides prospects from another. Type your own name to share one memory across clients, or to keep one per campaign.",
            "default": "link-building-prospects-ai-seen"
          },
          "discoveryTimeoutSecs": {
            "title": "Source run timeout, seconds (API only)",
            "minimum": 60,
            "maximum": 86400,
            "type": "integer",
            "description": "API only — hidden on the form. How long the source Store Actor may run before its partial results are used. Raise it when the status says the source stopped early.",
            "default": 3600
          },
          "slackWebhookUrl": {
            "title": "Slack webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Slack incoming-webhook URL for the digest."
          },
          "discordWebhookUrl": {
            "title": "Discord webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Discord channel webhook URL for the digest."
          },
          "teamsWebhookUrl": {
            "title": "Microsoft Teams webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Teams Workflows webhook URL for the digest."
          },
          "googleChatWebhookUrl": {
            "title": "Google Chat webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Google Chat space webhook URL for the digest."
          },
          "telegramUrl": {
            "title": "Telegram sendMessage link (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. The bot's sendMessage link with its chat_id."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}