openapi: 3.1.0 info: title: SpeedyIndex API version: "2.0" summary: Link indexing, index checking, backlink checking and Google rank tracking. description: | SpeedyIndex API v2 lets you submit URLs for indexing in Google and Yandex, check whether URLs are indexed in Google, Yandex and Bing, check donor pages for backlinks, and track Google rankings and AI Overview citations with the Rank Tracker. **Authentication.** Send your API key in the `Authorization` header as it is, without `Bearer`. Copy the key from the API section of your SpeedyIndex dashboard. POST, PUT and PATCH requests send JSON with `Content-Type: application/json`. **Responses.** Every response has a numeric `code`: `0` means success. Indexing endpoints use `1` insufficient balance or not found, `2` validation error, `3` rate limit or server error. Rank Tracker errors also have `error` (a stable machine-readable name) and `message`. **Rate limits.** Indexing, checking and backlink endpoints: 120 requests per minute per IP address. Rank Tracker: per API key, 600 reads (GET) and 120 writes a minute, at most 8 requests at once. Over a limit you get HTTP 429 with `Retry-After`. **Prices.** Google indexing runs in Pay per Indexed mode: 100 tokens per URL, 100% refund for URLs not indexed after 7 days. Yandex indexing: 20 tokens per URL, Pay per Indexed always on, refund after 15 days. A Rank Tracker check costs tokens per loaded results page; read current prices from `GET /v2/rank-tracker/limits`. Source: the official SpeedyIndex API documentation, https://app.speedyindex.com/Documentation.pdf contact: name: SpeedyIndex support email: support@speedyindex.com url: https://en.speedyindex.com/ license: name: Proprietary url: https://en.speedyindex.com/ termsOfService: https://en.speedyindex.com/privacy-policy/ x-logo: url: https://en.speedyindex.com/favicon.ico externalDocs: description: Official API documentation (PDF) url: https://app.speedyindex.com/Documentation.pdf servers: - url: https://api.speedyindex.com description: Production security: - apiKey: [] tags: - name: Account description: Balance and top-up invoices. - name: Indexing and index checks description: Tasks that submit URLs for indexing (Google, Yandex) or check index status (Google, Yandex, Bing). - name: Single URL indexing description: Submit one URL without creating a task manually. - name: Backlink checker description: Open donor pages and find links to your target domains. - name: Rank Tracker - reference description: Limits, prices, locations, groups and spending. - name: Rank Tracker - projects description: Sites tracked in Google with their regions, keywords, competitors and schedule. - name: Rank Tracker - regions description: Every keyword is checked in every region of its project. A removed region keeps its history. - name: Rank Tracker - keywords and tags description: Keywords are matched by text ignoring case and spaces. Tags are lowercase, up to 40 characters, at most 10 per keyword. - name: Rank Tracker - checks description: A check holds cost.max tokens when it starts and gives back what it did not spend when it ends. One check runs per project at a time. - name: Rank Tracker - results description: Positions, trends, keyword details with AI Overview data, and exports. Positions are kept for 365 days. - name: Rank Tracker - webhooks description: One webhook URL per account gets the events of all projects; a project's own webhook_url gets that project's events instead. paths: /v2/account: get: tags: [Account] operationId: getBalance summary: Check the balance description: Returns your current balance across all services. responses: "200": description: Balance content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } balance: type: object properties: tokens: { type: integer, description: Token balance } indexer: { type: integer, description: Indexing credits } checker: { type: integer, description: Index check credits } example: { code: 0, balance: { tokens: 5000, indexer: 10014495, checker: 100732 } } "401": { $ref: "#/components/responses/Unauthorized" } /v2/account/invoice/create: post: tags: [Account] operationId: createInvoice summary: Create an invoice to purchase tokens description: | Available packages: $5 (5,000), $20 (30,000), $50 (120,000), $100 (250,000), $250 (700,000), $500 (1,500,000 tokens). PayPal: +10% fee. YooKassa: USD to RUB (1 USD = 100 RUB), requires email. requestBody: required: true content: application/json: schema: type: object required: [price, method] properties: price: { type: number, enum: [5, 20, 50, 100, 250, 500], description: Package price in USD } method: { type: string, enum: [crypto, paypal, yookassa], description: Payment method } email: { type: string, format: email, description: Required for yookassa. Email for the payment receipt. } example: { price: 50, method: crypto } responses: "200": description: Invoice created content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } result: { type: string, format: uri, description: Payment invoice URL } tokens_amount: { type: integer, description: Tokens to be credited } price: { type: number, description: Final price (USD or RUB) } currency: { type: string, enum: [USD, RUB] } example: { code: 0, result: "https://pay.cryptocloud.plus/LJQ18AI1?lang=en", tokens_amount: 120000, price: 50, currency: USD } "401": { $ref: "#/components/responses/Unauthorized" } /v2/task/{search_engine}/{task_type}/create: post: tags: [Indexing and index checks] operationId: createTask summary: Create an indexing or index check task description: | `search_engine`: google, yandex, bing. `task_type`: indexer, checker. Bing supports checker only. Currently only the `pay_per_indexed: true` mode is available for Google indexing tasks; standard Google indexing is temporarily unavailable. Google indexer: 100 tokens/URL, 100% refund for unindexed links after 7 days. Yandex indexer: Pay per Indexed is always on, 20 tokens/URL, 100% refund for unindexed links after 15 days. Indexing credits are spent first (1 credit/URL); tokens are charged only for the URLs left uncovered. Drip Feed (google/indexer only): set `drip_feed: true` and `drip_feed_days` (2-30) to spread submissions across several days in equal daily batches. The number of URLs must be at least `drip_feed_days`. parameters: - $ref: "#/components/parameters/SearchEngine" - $ref: "#/components/parameters/TaskType" requestBody: required: true content: application/json: schema: type: object required: [urls] properties: title: { type: string, description: Task name } urls: { type: array, items: { type: string, format: uri }, maxItems: 10000 } pay_per_indexed: { type: boolean, description: "true for Pay per Indexed (google/indexer). Not needed for yandex/indexer." } drip_feed: { type: boolean, description: true to create a Drip Feed task (google/indexer only) } drip_feed_days: { type: integer, minimum: 2, maximum: 30, description: Required if drip_feed is true } example: { title: my task, urls: ["https://example.com/page1", "https://example.com/page2"], pay_per_indexed: true } responses: "200": description: "Task created (code 0), or refused: code 1 insufficient balance, code 2 validation error" content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } task_id: { type: string } type: { type: string, example: google/indexer } pay_per_indexed: { type: boolean } drip_feed: { type: boolean } drip_feed_days: { type: integer } example: { code: 0, task_id: 6609d023a3188540f09fec6c, type: google/indexer, pay_per_indexed: true } "401": { $ref: "#/components/responses/Unauthorized" } "402": { $ref: "#/components/responses/InsufficientBalance" } "413": { $ref: "#/components/responses/PayloadTooLarge" } "429": { $ref: "#/components/responses/RateLimited" } /v2/task/{search_engine}/{task_type}/list/{page}: get: tags: [Indexing and index checks] operationId: listTasks summary: List tasks description: | Up to 1000 tasks per page, newest first. Pages start at 0. `filter` (google/indexer only): standard, pay_per_indexed or drip_feed. Without a filter: standard and Pay per Indexed tasks (no Drip Feed). Combine `filter=drip_feed` with `pay_per_indexed=true|false` to filter Drip Feed tasks by type. parameters: - $ref: "#/components/parameters/SearchEngine" - $ref: "#/components/parameters/TaskType" - name: page in: path required: true schema: { type: integer, minimum: 0 } - name: filter in: query schema: { type: string, enum: [standard, pay_per_indexed, drip_feed] } - name: pay_per_indexed in: query description: With filter=drip_feed only. schema: { type: boolean } responses: "200": description: A page of tasks content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } page: { type: integer } last_page: { type: integer } result: { type: array, items: { $ref: "#/components/schemas/Task" } } example: code: 0 page: 0 last_page: 0 result: - { id: 65f8c7315752853b9171860a, size: 690, processed_count: 690, indexed_count: 279, title: my_urls.txt, is_completed: true, type: google/indexer, created_at: "2024-03-18T22:58:56.901Z" } "401": { $ref: "#/components/responses/Unauthorized" } /v2/task/{search_engine}/{task_type}/status: post: tags: [Indexing and index checks] operationId: getTaskStatus summary: Get the status of tasks description: | Provide either `task_id` or `task_ids`. Pay per Indexed tasks add status, tokens_charged, tokens_refunded and check_after. For a single Drip Feed task the response adds days, days_passed, is_stopped, child_task_ids and a `children` array. parameters: - $ref: "#/components/parameters/SearchEngine" - $ref: "#/components/parameters/TaskType" requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/TaskIdOrIds" } example: { task_id: 65f8c7305759855b9171860a } responses: "200": description: Task or tasks content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } result: oneOf: - $ref: "#/components/schemas/Task" - type: array items: { $ref: "#/components/schemas/Task" } example: code: 0 result: { id: 65f8c7305759855b9171860a, size: 690, processed_count: 690, indexed_count: 279, is_completed: true, title: my_urls.txt, type: google/indexer, created_at: "2024-03-18T22:58:56.901Z", pay_per_indexed: true, status: completed, tokens_charged: 69000, tokens_refunded: 41100, check_after: "2024-03-25T22:58:56.901Z" } "401": { $ref: "#/components/responses/Unauthorized" } /v2/task/{search_engine}/{task_type}/report: post: tags: [Indexing and index checks] operationId: getTaskReport summary: Download a task report description: Indexed and unindexed URL lists. For a Drip Feed task the lists are aggregated across all child tasks. parameters: - $ref: "#/components/parameters/SearchEngine" - $ref: "#/components/parameters/TaskType" requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/TaskIdBody" } example: { task_id: 653278d8a2b987d72b36f2a2 } responses: "200": description: Report content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } result: allOf: - $ref: "#/components/schemas/ReportBase" - type: object properties: indexed_links: { type: array, items: { type: string, format: uri } } unindexed_links: { type: array, items: { type: string, format: uri } } example: code: 0 result: { id: 653278d8a2b987d72b36f2a2, size: 3, processed_count: 3, indexed_links: ["https://example.com/page1"], unindexed_links: ["https://example.com/page2"], title: test, type: google/indexer, created_at: "2023-10-20T12:55:52.031Z", pay_per_indexed: true, tokens_charged: 300, tokens_refunded: 100 } "401": { $ref: "#/components/responses/Unauthorized" } /v2/task/{search_engine}/{task_type}/fullreport: post: tags: [Indexing and index checks] operationId: getTaskFullReport summary: Download a full task report with page titles description: | Same as the report, but each indexed link is `{url, title}`, where title is the page title in the search results of the selected engine (empty string if unavailable). Unindexed links are `{url, error_code}` for standard tasks and `{url}` for Pay per Indexed and Drip Feed tasks. error_code: -1 meta noindex found, 0 no errors, other values are the HTTP status found on the page. yandex/indexer: links are marked indexed only after the automatic final check 15 days after the task is created. parameters: - $ref: "#/components/parameters/SearchEngine" - $ref: "#/components/parameters/TaskType" requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/TaskIdBody" } example: { task_id: 653278d8a2b987d72b36f2a2 } responses: "200": description: Full report content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } result: allOf: - $ref: "#/components/schemas/ReportBase" - type: object properties: indexed_links: type: array items: type: object properties: url: { type: string, format: uri } title: { type: string } unindexed_links: type: array items: type: object properties: url: { type: string, format: uri } error_code: { type: integer } example: code: 0 result: { id: 653278d8a2b987d72b36f2a2, size: 3, processed_count: 3, indexed_links: [{ url: "https://example.com/page1", title: Page Title }], unindexed_links: [{ url: "https://example.com/page2" }], title: test, type: google/indexer, created_at: "2023-10-20T12:55:52.031Z", pay_per_indexed: true, tokens_charged: 300, tokens_refunded: 100 } "401": { $ref: "#/components/responses/Unauthorized" } /v2/task/backlink/checker/create: post: tags: [Backlink checker] operationId: createBacklinkCheck summary: Create a backlink check task description: Check donor pages for backlinks to your target domains. Up to 10,000 donor URLs and 50 target domains per task. requestBody: required: true content: application/json: schema: type: object required: [urls, target_domains] properties: title: { type: string } urls: { type: array, items: { type: string, format: uri }, maxItems: 10000, description: Donor URLs to check } target_domains: { type: array, items: { type: string }, maxItems: 50, example: [mydomain.com] } example: { title: backlink check, urls: ["https://donor1.com/page", "https://donor2.com/page"], target_domains: [mydomain.com] } responses: "200": description: "Task created (code 0), or refused: code 1 insufficient balance, code 2 validation error" content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } task_id: { type: string } type: { type: string, const: backlink/checker } example: { code: 0, task_id: 680abc123def456, type: backlink/checker } "401": { $ref: "#/components/responses/Unauthorized" } "402": { $ref: "#/components/responses/InsufficientBalance" } /v2/task/backlink/checker/status: post: tags: [Backlink checker] operationId: getBacklinkCheckStatus summary: Get the status of backlink check tasks requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/TaskIdOrIds" } example: { task_id: 680abc123def456 } responses: "200": description: Task or tasks content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } result: oneOf: - $ref: "#/components/schemas/BacklinkTask" - type: array items: { $ref: "#/components/schemas/BacklinkTask" } example: code: 0 result: { id: 680abc123def456, size: 100, processed_count: 100, title: backlink check, target_domains: [mydomain.com], is_completed: true, type: backlink/checker, created_at: "2024-10-20T12:55:52.031Z" } "401": { $ref: "#/components/responses/Unauthorized" } /v2/task/backlink/checker/report: post: tags: [Backlink checker] operationId: getBacklinkCheckReport summary: Download a backlink check report description: For each donor URL — HTTP status, Moz Domain Authority, Moz spam score, indexability and the links found to the target domains. Paged, up to 100 donors per page. requestBody: required: true content: application/json: schema: type: object required: [task_id] properties: task_id: { type: string } page: { type: integer, minimum: 0, default: 0 } limit: { type: integer, minimum: 1, maximum: 100 } example: { task_id: 680abc123def456, page: 0, limit: 50 } responses: "200": description: A page of the report content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } result: type: object properties: id: { type: string } size: { type: integer } processed_count: { type: integer } is_completed: { type: boolean } page: { type: integer } last_page: { type: integer } donors: { type: array, items: { $ref: "#/components/schemas/Donor" } } example: code: 0 result: id: 680abc123def456 size: 2 processed_count: 2 is_completed: true page: 0 last_page: 0 donors: - { donor_url: "https://donor1.com/page", processed: true, http_status: 200, moz_domain_authority: 45, moz_spam_score: 3, target_links: [{ url: "https://mydomain.com/article", anchor: read more, nofollow: false }] } "401": { $ref: "#/components/responses/Unauthorized" } /v2/{search_engine}/url: post: tags: [Single URL indexing] operationId: submitSingleUrl summary: Index a single link description: | `search_engine`: google or yandex. Google: pass `pay_per_indexed: true` for 100 tokens/URL with a 100% refund for unindexed links after 7 days; without it, standard indexing credits are used (1 credit/URL). Yandex: Pay per Indexed is always on (refund after 15 days). Indexing credits are spent first; when they run out, 20 tokens/URL are charged. The flag is accepted but has no effect. parameters: - name: search_engine in: path required: true example: google schema: { type: string, enum: [google, yandex] } requestBody: required: true content: application/json: schema: type: object required: [url] properties: url: { type: string, format: uri } pay_per_indexed: { type: boolean } example: { url: "https://example.com/page", pay_per_indexed: true } responses: "200": description: "code 0 success, 1 insufficient balance, 2 invalid URL, 3 daily limit exceeded or the task could not be created (the charge is refunded)" content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } task_id: { type: string, description: "Yandex: always returned. Google: only with pay_per_indexed: true." } type: { type: string, example: yandex/indexer } pay_per_indexed: { type: boolean } example: { code: 0, task_id: 6654a1b2c3d4e5f6a7b8c9d0, pay_per_indexed: true } "401": { $ref: "#/components/responses/Unauthorized" } # ---------------- Rank Tracker ---------------- /v2/rank-tracker/limits: get: tags: [Rank Tracker - reference] operationId: rtGetLimits summary: Limits, prices and balance description: Your account's limits and usage, current prices, balance and tokens held by running checks. Read limits from here rather than hardcoding them. responses: "200": description: Limits content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } limits: type: object properties: projects: { $ref: "#/components/schemas/LimitUsed" } tracked_rows: { $ref: "#/components/schemas/LimitUsed" } keywords_per_project: { type: integer } regions_per_project: { type: integer } tags_per_project: { type: integer } rows_per_check: { type: integer } keywords_per_request: { type: integer } prices: type: object properties: page: { type: integer, description: Tokens per results page } page_with_ai_overview: { type: integer, description: Tokens for the first page with an AI Overview } promo: { $ref: "#/components/schemas/Promo" } balance: { type: integer } tokens_held: { type: integer } depths: { type: array, items: { type: integer } } devices: { type: array, items: { type: string } } webhook_events: { type: array, items: { type: string } } example: code: 0 limits: { projects: { limit: 50, used: 12 }, tracked_rows: { limit: 100000, used: 8640 }, keywords_per_project: 1000, regions_per_project: 5, tags_per_project: 100, rows_per_check: 50000, keywords_per_request: 10000 } prices: { page: 4, page_with_ai_overview: 8, promo: null } balance: 250000 tokens_held: 1920 depths: [10, 30, 50, 100] devices: [desktop] webhook_events: [check.finished, check.failed, check.skipped] "401": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/locations: get: tags: [Rank Tracker - reference] operationId: rtSearchLocations summary: Find a location description: Google locations by the start of their name. Use canonical_name as a region's location and country_code as its gl. parameters: - { name: q, in: query, required: true, schema: { type: string }, example: new york } - { name: gl, in: query, schema: { type: string, minLength: 2, maxLength: 2 } } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 10 } } responses: "200": description: Locations content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } locations: type: array items: type: object properties: id: { type: integer } name: { type: string } canonical_name: { type: string } country_code: { type: string } target_type: { type: string } example: code: 0 locations: - { id: 1023191, name: New York, canonical_name: "New York,New York,United States", country_code: us, target_type: City } "401": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/groups: get: tags: [Rank Tracker - reference] operationId: rtListGroups summary: Project groups responses: "200": description: Groups with project counts; group "" is the projects without a group content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: type: array items: type: object properties: group: { type: string } projects: { type: integer } archived: { type: integer } example: { code: 0, items: [{ group: "", projects: 3, archived: 1 }, { group: Clients, projects: 9, archived: 0 }] } /v2/rank-tracker/usage: get: tags: [Rank Tracker - reference] operationId: rtGetUsage summary: Spending description: Tokens spent on checks by day and by project. The last 30 days by default, up to 366 days. parameters: - { name: from, in: query, schema: { type: string }, description: "YYYY-MM-DD (midnight in tz) or an ISO time" } - { name: to, in: query, schema: { type: string } } - { name: tz, in: query, schema: { type: string, default: UTC }, description: IANA time zone of the days } - { name: group, in: query, schema: { type: string } } - { name: project_id, in: query, schema: { type: string } } responses: "200": description: Spending content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } from: { type: string } to: { type: string } tz: { type: string } total: { type: object, properties: { tokens: { type: integer }, checks: { type: integer } } } days: { type: array, items: { type: object, properties: { date: { type: string }, tokens: { type: integer }, checks: { type: integer } } } } projects: { type: array, items: { type: object, properties: { project_id: { type: string }, project_name: { type: string }, group: { type: string }, tokens: { type: integer }, checks: { type: integer } } } } /v2/rank-tracker/projects: get: tags: [Rank Tracker - projects] operationId: rtListProjects summary: List projects parameters: - { name: sort, in: query, schema: { type: string, enum: [created, name, last_check, visibility], default: created } } - { name: archived, in: query, schema: { type: boolean } } - { name: group, in: query, schema: { type: string } } - { name: search, in: query, schema: { type: string }, description: Part of the name or domain } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 500, default: 100 } } - $ref: "#/components/parameters/Cursor" responses: "200": description: A page of projects content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: { type: array, items: { $ref: "#/components/schemas/Project" } } next_cursor: { $ref: "#/components/schemas/NextCursor" } total: { type: integer } "401": { $ref: "#/components/responses/RtError" } post: tags: [Rank Tracker - projects] operationId: rtCreateProject summary: Create a project description: | Creates a project, optionally with its keywords. If the keywords are refused (over a limit), the project is not created. Send `ref` (your own id) to make the request safe to repeat after a timeout: the same ref returns the same project with `repeated: true`. requestBody: required: true content: application/json: schema: type: object required: [domain, regions] properties: domain: { type: string, example: example.com } regions: { type: array, minItems: 1, items: { $ref: "#/components/schemas/RegionInput" } } name: { type: string, maxLength: 120 } depth: { type: integer, enum: [10, 30, 50, 100], default: 30 } competitors: { type: array, maxItems: 10, items: { type: string } } schedule: { $ref: "#/components/schemas/Schedule" } group: { type: string, maxLength: 80 } keywords: { $ref: "#/components/schemas/KeywordInputList" } tags: { type: array, items: { type: string } } webhook_url: { type: string, format: uri } check_emails: { type: boolean, default: false } ref: { type: string, maxLength: 200 } example: domain: example.com regions: [{ location: "New York,New York,United States", gl: us }] depth: 30 competitors: [competitor.com] schedule: { days: [1, 4], hour: 6, tz: Europe/London } group: Clients keywords: ["pizza delivery", { q: best pizza near me, tags: [local], target_url: "https://example.com/menu" }] ref: crm-4521 responses: "201": description: Project created content: application/json: schema: { $ref: "#/components/schemas/ProjectCreated" } "200": description: The project made earlier by a request with the same ref (repeated true) content: application/json: schema: { $ref: "#/components/schemas/ProjectCreated" } "400": { $ref: "#/components/responses/RtError" } "409": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/projects/{project_id}: parameters: - $ref: "#/components/parameters/ProjectId" get: tags: [Rank Tracker - projects] operationId: rtGetProject summary: Get a project description: The project with its tags, removed regions, the cost of its next check, and its running and last finished checks. responses: "200": description: Project content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } project: allOf: - $ref: "#/components/schemas/Project" - type: object properties: past_regions: { type: array, items: { $ref: "#/components/schemas/Region" } } tags: { type: array, items: { type: object, properties: { tag: { type: string }, count: { type: integer } } } } tag_colors: { type: object, additionalProperties: { type: string } } cost: { $ref: "#/components/schemas/Cost" } active_check: { oneOf: [{ $ref: "#/components/schemas/Check" }, { type: "null" }] } last_check: { oneOf: [{ $ref: "#/components/schemas/Check" }, { type: "null" }] } "404": { $ref: "#/components/responses/RtError" } patch: tags: [Rank Tracker - projects] operationId: rtUpdateProject summary: Update a project description: Changes the fields sent; the others stay. An archived project is not checked by its schedule until it is restored. requestBody: required: true content: application/json: schema: type: object properties: name: { type: string, maxLength: 120 } depth: { type: integer, enum: [10, 30, 50, 100] } competitors: { type: array, maxItems: 10, items: { type: string } } group: { type: string, maxLength: 80 } check_emails: { type: boolean } schedule: { oneOf: [{ $ref: "#/components/schemas/Schedule" }, { type: "null" }] } webhook_url: { type: [string, "null"], description: 'null or "" uses the account webhook' } archived: { type: boolean } example: { depth: 50, schedule: { days: [1, 2, 3, 4, 5], hour: 7, tz: America/New_York } } responses: "200": description: Project content: application/json: schema: { $ref: "#/components/schemas/ProjectEnvelope" } "400": { $ref: "#/components/responses/RtError" } "404": { $ref: "#/components/responses/RtError" } delete: tags: [Rank Tracker - projects] operationId: rtDeleteProject summary: Delete a project description: Deletes the project with its keywords and history. A running check is stopped and its unspent tokens come back. responses: "202": description: Deleting content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } id: { type: string } deleting: { type: boolean } example: { code: 0, id: 6720a1b2c3d4e5f6a7b8c9d0, deleting: true } "404": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/projects/bulk: post: tags: [Rank Tracker - projects] operationId: rtBulkProjects summary: Act on many projects description: One action for up to 500 projects; each succeeds or fails on its own. `check` queues a check of every project. requestBody: required: true content: application/json: schema: type: object required: [ids, action] properties: ids: { type: array, maxItems: 500, items: { type: string } } action: { type: string, enum: [archive, unarchive, delete, set_schedule, set_group, check] } schedule: { oneOf: [{ $ref: "#/components/schemas/Schedule" }, { type: "null" }] } group: { type: string } example: { ids: [6720a1b2c3d4e5f6a7b8c9d0, 6720a1b2c3d4e5f6a7b8c9ff], action: check } responses: "200": description: Results per project content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } results: type: array items: type: object properties: id: { type: string } ok: { type: boolean } error: { type: string } message: { type: string } check_id: { type: string } /v2/rank-tracker/projects/summary: post: tags: [Rank Tracker - projects] operationId: rtProjectsSummary summary: Status of many projects description: A light status of up to 500 projects at once, for dashboards that poll. requestBody: required: true content: application/json: schema: type: object required: [project_ids] properties: project_ids: { type: array, maxItems: 500, items: { type: string } } responses: "200": description: Statuses content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: type: array items: type: object properties: id: { type: string } status: { type: string, enum: [idle, checking] } archived: { type: boolean } last_check_at: { type: [string, "null"], format: date-time } next_check_at: { type: [string, "null"], format: date-time } last_summaries: { type: object } not_found: { type: array, items: { type: string } } /v2/rank-tracker/projects/{project_id}/regions: parameters: - $ref: "#/components/parameters/ProjectId" post: tags: [Rank Tracker - regions] operationId: rtAddRegion summary: Add a region description: Adds a region (keywords x 1 rows of the tracked rows limit). Adding a region the project had before brings its history back. requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/RegionInput" } example: { location: "London,England,United Kingdom", gl: gb } responses: "201": description: Region added content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } region: { $ref: "#/components/schemas/Region" } project: { $ref: "#/components/schemas/Project" } "400": { $ref: "#/components/responses/RtError" } "409": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/projects/{project_id}/regions/{region_id}: delete: tags: [Rank Tracker - regions] operationId: rtRemoveRegion summary: Remove a region description: Removes a region; its history stays. Not the project's last region (400 last_region) and not while a check runs (409). parameters: - $ref: "#/components/parameters/ProjectId" - { name: region_id, in: path, required: true, schema: { type: string } } responses: "200": description: Project content: application/json: schema: { $ref: "#/components/schemas/ProjectEnvelope" } "400": { $ref: "#/components/responses/RtError" } "409": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/projects/{project_id}/keywords: parameters: - $ref: "#/components/parameters/ProjectId" get: tags: [Rank Tracker - keywords and tags] operationId: rtListKeywords summary: List keywords description: The project's keywords, oldest first. parameters: - { name: tag, in: query, schema: { type: string } } - { name: search, in: query, schema: { type: string } } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 5000, default: 1000 } } - $ref: "#/components/parameters/Cursor" responses: "200": description: A page of keywords content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: { type: array, items: { $ref: "#/components/schemas/Keyword" } } next_cursor: { $ref: "#/components/schemas/NextCursor" } total: { type: integer } post: tags: [Rank Tracker - keywords and tags] operationId: rtAddKeywords summary: Add keywords description: | Adds keywords. A keyword the project already has gets the new tags and the target_url if one is sent. A keyword that is too long or has a target_url on another domain is skipped and listed in errors; over a limit nothing is added (400). requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/KeywordsBody" } example: { keywords: ["pizza delivery", { q: best pizza near me, tags: [local], target_url: "https://example.com/menu" }], tags: [q4] } responses: "200": description: Counts per status content: application/json: schema: { $ref: "#/components/schemas/KeywordsResult" } "400": { $ref: "#/components/responses/RtError" } put: tags: [Rank Tracker - keywords and tags] operationId: rtReplaceKeywords summary: Replace the keyword list description: Makes the project's keywords exactly the ones sent. Missing keywords are removed with their history. If any keyword is refused, nothing changes (400 invalid_keywords). requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/KeywordsBody" } example: { keywords: ["pizza delivery", "pizza near me open now"] } responses: "200": description: Counts content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } added: { type: integer } removed: { type: integer } updated: { type: integer } unchanged: { type: integer } items: { type: array, items: { $ref: "#/components/schemas/KeywordStatus" } } "400": { $ref: "#/components/responses/RtError" } patch: tags: [Rank Tracker - keywords and tags] operationId: rtEditKeywords summary: Edit tags and target URLs description: Adds or removes tags and sets the target URL of many keywords at once. The keyword text never changes. requestBody: required: true content: application/json: schema: type: object required: [ids] properties: ids: { type: array, maxItems: 10000, items: { type: string } } add_tags: { type: array, items: { type: string } } remove_tags: { type: array, items: { type: string } } target_url: { type: [string, "null"], description: 'A URL on the project domain; null or "" removes it' } example: { ids: [6720a1b2c3d4e5f6a7b8c9e0, 6720a1b2c3d4e5f6a7b8c9e1], add_tags: [priority], remove_tags: [q4] } responses: "200": description: Matched keywords content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } matched: { type: integer } /v2/rank-tracker/projects/{project_id}/keywords/delete: post: tags: [Rank Tracker - keywords and tags] operationId: rtDeleteKeywords summary: Delete keywords description: Deletes keywords with their history. parameters: - $ref: "#/components/parameters/ProjectId" requestBody: required: true content: application/json: schema: type: object required: [ids] properties: ids: { type: array, items: { type: string } } responses: "200": description: Deleted count content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } deleted: { type: integer } /v2/rank-tracker/projects/{project_id}/keywords/{keyword_id}/history: get: tags: [Rank Tracker - keywords and tags] operationId: rtKeywordHistory summary: Keyword history description: A keyword's positions and its competitors' over the region's finished checks, oldest first. parameters: - $ref: "#/components/parameters/ProjectId" - $ref: "#/components/parameters/KeywordId" - $ref: "#/components/parameters/RegionIdQuery" - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 90, default: 90 } } responses: "200": description: History content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } region: { $ref: "#/components/schemas/Region" } items: type: array items: type: object properties: check_id: { type: string } at: { type: string, format: date-time } position: { type: [integer, "null"] } approx: { type: boolean } unknown: { type: boolean } url: { type: [string, "null"] } competitors: { type: object, additionalProperties: { type: [integer, "null"] } } /v2/rank-tracker/projects/{project_id}/tags: get: tags: [Rank Tracker - keywords and tags] operationId: rtListTags summary: List tags parameters: - $ref: "#/components/parameters/ProjectId" responses: "200": description: Tags with keyword counts and colors content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: type: array items: type: object properties: tag: { type: string } count: { type: integer } color: { type: [string, "null"] } /v2/rank-tracker/projects/{project_id}/tags/rename: post: tags: [Rank Tracker - keywords and tags] operationId: rtRenameTag summary: Rename a tag description: Renames a tag on every keyword. Renaming to an existing tag merges the two; an empty `to` removes the tag. parameters: - $ref: "#/components/parameters/ProjectId" requestBody: required: true content: application/json: schema: type: object required: [from, to] properties: from: { type: string } to: { type: string } example: { from: local, to: near me } responses: "200": description: The project's tags content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } tags: { type: array, items: { type: string } } /v2/rank-tracker/projects/{project_id}/tags/color: post: tags: [Rank Tracker - keywords and tags] operationId: rtSetTagColor summary: Set a tag's color parameters: - $ref: "#/components/parameters/ProjectId" requestBody: required: true content: application/json: schema: type: object required: [tag, color] properties: tag: { type: string } color: type: [string, "null"] enum: [green, teal, blue, violet, pink, red, orange, amber, gray, null] example: { tag: brand, color: violet } responses: "200": description: Colors by tag content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } tag_colors: { type: object, additionalProperties: { type: string } } /v2/rank-tracker/projects/{project_id}/cost: get: tags: [Rank Tracker - checks] operationId: rtGetCheckCost summary: Cost of a check description: What the project's next check can cost, in tokens. The balance must cover cost.max. parameters: - $ref: "#/components/parameters/ProjectId" - { name: region_id, in: query, schema: { type: string }, description: Only this region; all regions by default } responses: "200": description: Cost content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } cost: { $ref: "#/components/schemas/Cost" } example: { code: 0, cost: { min: 480, max: 1920, rows: 120, promo: null } } /v2/rank-tracker/projects/{project_id}/checks: parameters: - $ref: "#/components/parameters/ProjectId" post: tags: [Rank Tracker - checks] operationId: rtStartCheck summary: Run a check description: Starts a check of all regions or one. Not enough tokens for cost.max returns 402 insufficient_balance; a check already running returns 409 already_running with its check_id. requestBody: content: application/json: schema: type: object properties: region_id: { type: string } example: {} responses: "202": description: The check, queued content: application/json: schema: { $ref: "#/components/schemas/CheckEnvelope" } "402": { $ref: "#/components/responses/RtError" } "409": { $ref: "#/components/responses/RtError" } get: tags: [Rank Tracker - checks] operationId: rtListChecks summary: List checks description: The project's checks, newest first. parameters: - { name: region_id, in: query, schema: { type: string } } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } } - $ref: "#/components/parameters/Cursor" responses: "200": description: A page of checks content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: { type: array, items: { $ref: "#/components/schemas/Check" } } next_cursor: { $ref: "#/components/schemas/NextCursor" } /v2/rank-tracker/checks/{check_id}: get: tags: [Rank Tracker - checks] operationId: rtGetCheck summary: Get a check description: A check with its progress while it runs. Poll every 15-60 seconds, or wait for the webhook. parameters: - $ref: "#/components/parameters/CheckId" responses: "200": description: Check content: application/json: schema: { $ref: "#/components/schemas/CheckEnvelope" } "404": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/checks/status: post: tags: [Rank Tracker - checks] operationId: rtChecksStatus summary: Status of many checks requestBody: required: true content: application/json: schema: type: object required: [check_ids] properties: check_ids: { type: array, maxItems: 1000, items: { type: string } } responses: "200": description: Checks content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: { type: array, items: { $ref: "#/components/schemas/Check" } } not_found: { type: array, items: { type: string } } /v2/rank-tracker/checks/{check_id}/cancel: post: tags: [Rank Tracker - checks] operationId: rtCancelCheck summary: Cancel a check description: Stops a check. What it has checked stays (partial, cancelled); unspent tokens come back. A finished check returns 409 check_finished. parameters: - $ref: "#/components/parameters/CheckId" responses: "202": description: The check, cancel_requested true content: application/json: schema: { $ref: "#/components/schemas/CheckEnvelope" } "409": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/projects/{project_id}/positions: get: tags: [Rank Tracker - results] operationId: rtGetPositions summary: Positions description: Every keyword's result in a check and region, with the change against the region's previous check. The latest finished check by default. parameters: - $ref: "#/components/parameters/ProjectId" - $ref: "#/components/parameters/RegionIdQuery" - { name: check_id, in: query, schema: { type: string, default: latest } } - { name: tag, in: query, schema: { type: string } } - { name: changed_only, in: query, schema: { type: string, enum: [up, down, new, lost] }, description: Only keywords that moved this way } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 1000, default: 500 } } - $ref: "#/components/parameters/Cursor" responses: "200": description: A page of positions content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } check: { oneOf: [{ $ref: "#/components/schemas/Check" }, { type: "null" }] } region: { $ref: "#/components/schemas/Region" } items: { type: array, items: { $ref: "#/components/schemas/Position" } } next_cursor: { $ref: "#/components/schemas/NextCursor" } "410": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/projects/{project_id}/trend: get: tags: [Rank Tracker - results] operationId: rtGetTrend summary: Trend description: A region's summary over its last 90 finished checks, oldest first. parameters: - $ref: "#/components/parameters/ProjectId" - $ref: "#/components/parameters/RegionIdQuery" responses: "200": description: Trend content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } region: { $ref: "#/components/schemas/Region" } items: type: array items: allOf: - type: object properties: check_id: { type: string } at: { type: string, format: date-time } partial: { type: boolean } depth: { type: integer } - $ref: "#/components/schemas/Summary" /v2/rank-tracker/checks/{check_id}/keywords/{keyword_id}: get: tags: [Rank Tracker - results] operationId: rtGetKeywordDetails summary: Keyword in a check description: One keyword's result in a finished check with the top 10 domains and the AI Overview (sources, whether the site is cited and how). The full overview text (blocks) is kept for the region's latest check only. parameters: - $ref: "#/components/parameters/CheckId" - $ref: "#/components/parameters/KeywordId" - $ref: "#/components/parameters/RegionIdQuery" responses: "200": description: Keyword result content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } check_id: { type: string } region: { $ref: "#/components/schemas/Region" } keyword: allOf: - $ref: "#/components/schemas/Position" - type: object properties: top10: { type: array, items: { type: string } } ai_overview: oneOf: - type: "null" - type: object properties: present: { type: boolean } cited: { type: boolean } mentioned: { type: boolean } place: { type: [integer, "null"] } url: { type: [string, "null"] } quotes: { type: array, items: { type: string } } sources: type: array items: type: object properties: url: { type: string } host: { type: string } title: { type: [string, "null"] } name: { type: [string, "null"] } owner: { type: [string, "null"] } blocks: { description: Full overview text while this is the region's latest check; null after } /v2/rank-tracker/projects/{project_id}/export: get: tags: [Rank Tracker - results] operationId: rtExport summary: Export positions description: All positions of a check and region in one streamed file, CSV (UTF-8 with BOM) or JSON. At most 2 exports at once per account (429 too_many_heavy_requests). parameters: - $ref: "#/components/parameters/ProjectId" - { name: format, in: query, schema: { type: string, enum: [csv, json], default: csv } } - { name: check_id, in: query, schema: { type: string } } - $ref: "#/components/parameters/RegionIdQuery" - { name: tag, in: query, schema: { type: string } } responses: "200": description: The export file content: text/csv: schema: { type: string } application/json: schema: { type: object } "429": { $ref: "#/components/responses/RtError" } /v2/rank-tracker/webhook: get: tags: [Rank Tracker - webhooks] operationId: rtGetWebhook summary: Get the webhook responses: "200": description: The account webhook content: application/json: schema: { $ref: "#/components/schemas/WebhookEnvelope" } put: tags: [Rank Tracker - webhooks] operationId: rtSetWebhook summary: Set the webhook description: Sets the URL and events. The first time, the response has the signing `secret`; store it, it is not shown again. requestBody: required: true content: application/json: schema: type: object required: [url] properties: url: { type: string, format: uri, description: A public https URL } events: { type: array, items: { type: string, enum: [check.finished, check.failed, check.skipped] }, description: "None or [] for all" } example: { url: "https://hooks.example.com/speedyindex", events: [check.finished, check.failed] } responses: "200": description: Webhook (and the secret the first time) content: application/json: schema: allOf: - $ref: "#/components/schemas/WebhookEnvelope" - type: object properties: secret: { type: string, description: "whsec_..., the first time only" } delete: tags: [Rank Tracker - webhooks] operationId: rtDeleteWebhook summary: Remove the webhook description: Stops the account's events. Projects with their own webhook_url still get theirs. The secret stays. responses: "200": description: Webhook content: application/json: schema: { $ref: "#/components/schemas/WebhookEnvelope" } /v2/rank-tracker/webhook/secret: post: tags: [Rank Tracker - webhooks] operationId: rtRotateWebhookSecret summary: Rotate the secret description: Makes a new signing secret. For 24 hours events are signed with both, newest first. responses: "200": description: New secret content: application/json: schema: allOf: - $ref: "#/components/schemas/WebhookEnvelope" - type: object properties: secret: { type: string } /v2/rank-tracker/webhook/test: post: tags: [Rank Tracker - webhooks] operationId: rtTestWebhook summary: Send a test event description: Sends a signed webhook.test event now and returns how your server replied. requestBody: content: application/json: schema: type: object properties: url: { type: string, format: uri, description: Another URL to test; the account's by default } responses: "200": description: Delivery result content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } delivery: type: object properties: id: { type: string } event_id: { type: string } url: { type: string } ok: { type: boolean } status: { type: [integer, "null"] } error: { type: [string, "null"] } /v2/rank-tracker/webhook/deliveries: get: tags: [Rank Tracker - webhooks] operationId: rtListDeliveries summary: Deliveries description: Events sent in the last 7 days, newest first, with their state. parameters: - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } } - $ref: "#/components/parameters/Cursor" responses: "200": description: A page of deliveries content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } items: type: array items: type: object properties: id: { type: string } event_id: { type: string } event: { type: string } url: { type: string } state: { type: string, enum: [pending, sending, done, failed] } attempts: { type: integer } last_status: { type: [integer, "null"] } last_error: { type: [string, "null"] } created_at: { type: string, format: date-time } delivered_at: { type: [string, "null"], format: date-time } next_at: { type: [string, "null"], format: date-time } project_id: { type: string } check_id: { type: string } payload: { type: object } next_cursor: { $ref: "#/components/schemas/NextCursor" } /v2/rank-tracker/webhook/deliveries/{delivery_id}/redeliver: post: tags: [Rank Tracker - webhooks] operationId: rtRedeliver summary: Send a delivery again description: Sends an event again with the same event_id; retries start anew. parameters: - { name: delivery_id, in: path, required: true, schema: { type: string } } responses: "202": description: Queued content: application/json: schema: type: object properties: code: { $ref: "#/components/schemas/Code" } id: { type: string } state: { type: string, const: pending } webhooks: check.finished: post: operationId: onCheckFinished summary: A check is done (cancelled and partial ones too) description: | Sent as a POST with a JSON body to your https URL. Any 2xx within 10 seconds is a delivery; otherwise it is retried after 1, 5 and 30 minutes. An event may arrive more than once: process each event_id once. `X-SpeedyIndex-Signature` is `sha256=` plus the hex HMAC-SHA256 of the raw body with your webhook secret; during a rotation it has two comma-separated signatures, the new one first. parameters: - $ref: "#/components/parameters/WebhookSignature" requestBody: content: application/json: schema: { $ref: "#/components/schemas/CheckFinishedEvent" } responses: "200": { description: Received } check.failed: post: operationId: onCheckFailed summary: A check failed parameters: - $ref: "#/components/parameters/WebhookSignature" requestBody: content: application/json: schema: type: object properties: event_id: { type: string } event: { type: string, const: check.failed } created_at: { type: string, format: date-time } project_id: { type: string } project_name: { type: string } group: { type: string } check_id: { type: string } trigger: { type: string } error: { type: string } tokens_spent: { type: integer } responses: "200": { description: Received } check.skipped: post: operationId: onCheckSkipped summary: A scheduled check did not run description: reason is insufficient_balance (with its check_id), still_running or serp_unavailable. parameters: - $ref: "#/components/parameters/WebhookSignature" requestBody: content: application/json: schema: type: object properties: event_id: { type: string } event: { type: string, const: check.skipped } created_at: { type: string, format: date-time } project_id: { type: string } project_name: { type: string } group: { type: string } reason: { type: string, enum: [insufficient_balance, still_running, serp_unavailable] } check_id: { type: string } scheduled_at: { type: string, format: date-time } responses: "200": { description: Received } components: securitySchemes: apiKey: type: apiKey in: header name: Authorization description: Your SpeedyIndex API key, sent as it is (no Bearer prefix). parameters: SearchEngine: name: search_engine in: path required: true description: google, yandex or bing (bing supports checker only) example: google schema: { type: string, enum: [google, yandex, bing] } TaskType: name: task_type in: path required: true description: indexer (submit for indexing) or checker (index status check) example: indexer schema: { type: string, enum: [indexer, checker] } ProjectId: name: project_id in: path required: true example: 6720a1b2c3d4e5f6a7b8c9d0 schema: { type: string, pattern: "^[0-9a-f]{24}$" } CheckId: name: check_id in: path required: true example: 6720b4f0c3d4e5f6a7b8ca01 schema: { type: string, pattern: "^[0-9a-f]{24}$" } KeywordId: name: keyword_id in: path required: true example: 6720a1b2c3d4e5f6a7b8c9e0 schema: { type: string, pattern: "^[0-9a-f]{24}$" } RegionIdQuery: name: region_id in: query description: A current or removed region (its history stays); the project's first region by default. schema: { type: string } Cursor: name: cursor in: query description: next_cursor of the previous page. Opaque; only next_cursor null means the end. schema: { type: string } WebhookSignature: name: X-SpeedyIndex-Signature in: header required: true schema: { type: string, example: "sha256=5f2b..." } responses: Unauthorized: description: The API key is missing or wrong content: application/json: schema: { $ref: "#/components/schemas/Error" } InsufficientBalance: description: Not enough balance content: application/json: schema: { $ref: "#/components/schemas/Error" } PayloadTooLarge: description: The request body is too large content: application/json: schema: { $ref: "#/components/schemas/Error" } RateLimited: description: Rate limit exceeded; wait for Retry-After seconds headers: Retry-After: schema: { type: integer } content: application/json: schema: { $ref: "#/components/schemas/Error" } RtError: description: | Rank Tracker error. `error` is a stable name to branch on, for example invalid_domain, projects_limit, archived, no_api_key, invalid_api_key, insufficient_balance, project_not_found, already_running, ref_conflict, ref_in_progress, check_finished, rows_expired, body_too_large, rate_limited, too_many_heavy_requests, internal_error, serp_unavailable. content: application/json: schema: { $ref: "#/components/schemas/Error" } example: { code: 10, error: already_running, message: A check of this project is running, check_id: 6720b4f0c3d4e5f6a7b8ca01 } schemas: Code: type: integer description: 0 on success; non-zero on error. Error: type: object properties: code: { type: integer } error: { type: string, description: Machine-readable error name (Rank Tracker) } message: { type: string } additionalProperties: true TaskIdBody: type: object required: [task_id] properties: task_id: { type: string } TaskIdOrIds: type: object properties: task_id: { type: string, description: A single task id (required if task_ids is not sent) } task_ids: { type: array, maxItems: 1000, items: { type: string }, description: Up to 1000 task ids (required if task_id is not sent) } Task: type: object properties: id: { type: string } size: { type: integer, description: Total URLs in the task } processed_count: { type: integer } indexed_count: { type: integer } title: { type: string } is_completed: { type: boolean } type: { type: string, example: google/indexer } created_at: { type: string, format: date-time } pay_per_indexed: { type: boolean, description: Pay per Indexed tasks only } status: { type: string, description: Pay per Indexed tasks only } tokens_charged: { type: integer, description: Pay per Indexed tasks only } tokens_refunded: { type: integer, description: Pay per Indexed tasks only } check_after: { type: string, format: date-time, description: Pay per Indexed tasks only. When the final index check runs. } days: { type: integer, description: Drip Feed tasks only } days_passed: { type: integer, description: Drip Feed tasks only } is_stopped: { type: boolean, description: Drip Feed tasks only } child_task_ids: { type: array, items: { type: string }, description: Drip Feed tasks only } children: { type: array, items: { $ref: "#/components/schemas/Task" }, description: "Drip Feed, single task_id only" } ReportBase: type: object properties: id: { type: string } size: { type: integer } processed_count: { type: integer } type: { type: string } title: { type: string } created_at: { type: string, format: date-time } pay_per_indexed: { type: boolean } tokens_charged: { type: integer } tokens_refunded: { type: integer } drip_feed: { type: boolean } days: { type: integer } days_passed: { type: integer } BacklinkTask: type: object properties: id: { type: string } size: { type: integer } processed_count: { type: integer } title: { type: string } target_domains: { type: array, items: { type: string } } is_completed: { type: boolean } type: { type: string, const: backlink/checker } created_at: { type: string, format: date-time } Donor: type: object properties: donor_url: { type: string, format: uri } processed: { type: boolean } error: { type: [string, "null"] } http_status: { type: [integer, "null"] } redirect_chain: { type: array, items: { type: object } } canonical_url: { type: [string, "null"] } page_language: { type: [string, "null"] } moz_domain_authority: { type: [integer, "null"] } moz_spam_score: { type: [integer, "null"] } internal_links_count: { type: [integer, "null"] } external_links_count: { type: [integer, "null"] } indexing: { type: object, description: "Whether search engines may index the page, by engine" } target_links: type: array items: type: object properties: url: { type: string } anchor: { type: string } anchor_type: { type: string } nofollow: { type: boolean } is_ugc: { type: boolean } is_sponsored: { type: boolean } LimitUsed: type: object properties: limit: { type: integer } used: { type: integer } Promo: type: [object, "null"] properties: discount: { type: number } until: { type: string, format: date-time } Cost: type: object properties: min: { type: integer, description: One results page per row } max: { type: integer, description: Held from the balance when the check starts } rows: { type: integer, description: Keywords x regions } promo: { $ref: "#/components/schemas/Promo" } NextCursor: type: [string, "null"] description: Pass as cursor for the next page; null means the end. Schedule: type: object required: [days, hour] properties: days: { type: array, items: { type: integer, minimum: 0, maximum: 6 }, description: "Days of the week, 0 is Sunday" } hour: { type: integer, minimum: 0, maximum: 23 } tz: { type: string, description: "IANA time zone, e.g. Europe/London" } RegionInput: type: object required: [location, gl] properties: location: { type: string, description: canonical_name from GET /v2/rank-tracker/locations } gl: { type: string, minLength: 2, maxLength: 2 } device: { type: string, enum: [desktop], default: desktop } Region: type: object properties: id: { type: string } location: { type: string } gl: { type: string } device: { type: string } google_domain: { type: string } label: { type: string } Summary: type: object description: A region's totals in a check. Keywords whose page failed to load are left out of every metric. properties: total: { type: integer } known: { type: [integer, "null"] } found: { type: integer } visibility: { type: number, description: "0-100%: position 1 counts 100%, 2 85%, 3 60%, 4-5 50%, 6-7 30%, 8-10 20%, 11-20 10%, 21-50 5%" } avg_position: { type: [number, "null"] } top3: { type: integer } top10: { type: integer } ai_present: { type: [integer, "null"] } ai_cited: { type: [integer, "null"] } distribution: type: object properties: top3: { type: integer } top10: { type: integer } top20: { type: integer } top50: { type: integer } top100: { type: integer } not_found: { type: integer } Project: type: object properties: id: { type: string } name: { type: string } domain: { type: string } group: { type: string } ref: { type: [string, "null"] } source: { type: string, enum: [api, cabinet] } depth: { type: integer, enum: [10, 30, 50, 100] } competitors: { type: array, items: { type: string } } regions: { type: array, items: { $ref: "#/components/schemas/Region" } } schedule: { oneOf: [{ $ref: "#/components/schemas/Schedule" }, { type: "null" }] } status: { type: string, enum: [idle, checking] } archived: { type: boolean } keywords_count: { type: integer } last_check_at: { type: [string, "null"], format: date-time } next_check_at: { type: [string, "null"], format: date-time } last_summaries: type: object description: By region id additionalProperties: type: object properties: check_id: { type: string } at: { type: string, format: date-time } summary: { $ref: "#/components/schemas/Summary" } change: { type: object } last_skipped: { type: [object, "null"] } webhook_url: { type: [string, "null"] } check_emails: { type: boolean } created_at: { type: string, format: date-time } ProjectEnvelope: type: object properties: code: { $ref: "#/components/schemas/Code" } project: { $ref: "#/components/schemas/Project" } ProjectCreated: type: object properties: code: { $ref: "#/components/schemas/Code" } project: { $ref: "#/components/schemas/Project" } keywords: { oneOf: [{ $ref: "#/components/schemas/KeywordsResult" }, { type: "null" }] } repeated: { type: boolean } Check: type: object properties: id: { type: string } project_id: { type: string } trigger: { type: string, enum: [manual, schedule, queued] } source: { type: string, enum: [api, cabinet] } status: { type: string, enum: [queued, running, done, failed] } error: { type: [string, "null"] } partial: { type: boolean } cancelled: { type: boolean } cancel_requested: { type: boolean } created_at: { type: string, format: date-time } started_at: { type: [string, "null"], format: date-time } finished_at: { type: [string, "null"], format: date-time } depth: { type: integer } rows: { type: integer } regions: { type: array, items: { $ref: "#/components/schemas/Region" } } summaries: { type: object, additionalProperties: { $ref: "#/components/schemas/Summary" } } tokens_spent: { type: integer } tokens_held: { type: [integer, "null"] } progress: type: [object, "null"] properties: keywords: { type: integer } wave: { type: integer } wave_keywords: { type: integer } wave_done: { type: integer } retry: { type: boolean } retry_at: { type: [string, "null"], format: date-time } failed_pages: { type: integer } eta_sec: { type: [integer, "null"] } rows_expire_at: { type: string, format: date-time } CheckEnvelope: type: object properties: code: { $ref: "#/components/schemas/Code" } check: { $ref: "#/components/schemas/Check" } KeywordInputList: type: array maxItems: 10000 items: oneOf: - type: string maxLength: 300 - type: object required: [q] properties: q: { type: string, maxLength: 300 } tags: { type: array, maxItems: 10, items: { type: string, maxLength: 40 } } target_url: { type: string, format: uri } KeywordsBody: type: object required: [keywords] properties: keywords: { $ref: "#/components/schemas/KeywordInputList" } tags: { type: array, items: { type: string } } KeywordStatus: type: object properties: index: { type: integer } id: { type: string } q: { type: string } status: { type: string, enum: [added, updated, unchanged, skipped] } KeywordsResult: type: object properties: code: { $ref: "#/components/schemas/Code" } added: { type: integer } updated: { type: integer } skipped: { type: integer } errors: type: array items: type: object properties: index: { type: integer } q: { type: string } error: { type: string, enum: [too_long, invalid_target_url] } items: { type: array, items: { $ref: "#/components/schemas/KeywordStatus" } } Keyword: type: object properties: id: { type: string } q: { type: string } tags: { type: array, items: { type: string } } target_url: { type: [string, "null"] } created_at: { type: string, format: date-time } Position: type: object properties: keyword_id: { type: string } q: { type: string } tags: { type: array, items: { type: string } } target_url: { type: [string, "null"] } status: { type: string, enum: [checked, pending, not_checked] } position: { type: [integer, "null"] } found: { type: boolean } unknown: { type: boolean } approx: { type: boolean } url: { type: [string, "null"] } urls: { type: [array, "null"], items: { type: object, properties: { url: { type: string }, position: { type: integer } } } } prev_position: { type: [integer, "null"] } prev_found: { type: [boolean, "null"] } change: { type: [integer, "null"] } movement: { type: [string, "null"], enum: [up, down, same, new, lost, null] } competitors: { type: [object, "null"] } features: { type: [array, "null"], items: { type: string } } ai_present: { type: boolean } ai_cited: { type: boolean } snippet_own: { type: boolean } snippet_owner: { type: [string, "null"] } wrong_url: { type: [boolean, "null"] } cannibal: { type: [boolean, "null"] } compact: { type: boolean } WebhookEnvelope: type: object properties: code: { $ref: "#/components/schemas/Code" } webhook: type: object properties: url: { type: [string, "null"] } events: { type: array, items: { type: string } } has_secret: { type: boolean } previous_secret_valid_until: { type: [string, "null"], format: date-time } CheckFinishedEvent: type: object properties: event_id: { type: string } event: { type: string, const: check.finished } created_at: { type: string, format: date-time } project_id: { type: string } project_name: { type: string } group: { type: string } project_ref: { type: [string, "null"] } check_id: { type: string } trigger: { type: string } status: { type: string } partial: { type: boolean } cancelled: { type: boolean } error: { type: [string, "null"] } summaries: { type: object } tokens_spent: { type: integer } finished_at: { type: string, format: date-time }