NewIntroducing semantic snapshotsPair every capture with structured DOM data →

For sites in more than one language

Check that every language version points at all the others

Read each language version's hreflang tags and confirm they all point at one another.

The problem

hreflang only works when it is reciprocal: the English page lists the German one and the German page lists the English one back. One template that drops a locale, or one market that launches on its own CMS, breaks the set, and search engines go back to guessing which version to show.

How domscout handles it

Two extract fields read every link[rel='alternate'][hreflang] on the rendered page: one collects the language codes and the other the URLs, in the same order. Run the request for each language version, then check that each one lists all the others and itself, and that x-default is there if you use it. Once every page's tags are in one place, the comparison is a few lines of code.

Where it shows up

  • A release check across every localised home page.
  • An audit when a new market or language launches.
  • A check after moving locales between domains or folders.

The request

POST /scrape. extract starts at Pro and costs 2 credits a page. To check many pages in one request, /batch on Business takes up to 100.

cURL
curl -X POST https://api.domscout.io/scrape \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.example.com/en/",
    "extractMarkdown": false,
    "extract": {
      "fields": {
        "languages": {
          "selector": "link[rel='\''alternate'\''][hreflang]",
          "type": "attribute",
          "attribute": "hreflang",
          "all": true
        },
        "urls": {
          "selector": "link[rel='\''alternate'\''][hreflang]",
          "type": "attribute",
          "attribute": "href",
          "all": true
        }
      }
    }
  }'
Node.js
const response = await fetch("https://api.domscout.io/scrape", {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": "YOUR_API_KEY" },
  body: JSON.stringify({
    url: "https://www.example.com/en/",
    extractMarkdown: false,
    extract: {
      fields: {
        languages: {
          selector: "link[rel='alternate'][hreflang]",
          type: "attribute",
          attribute: "hreflang",
          all: true
        },
        urls: {
          selector: "link[rel='alternate'][hreflang]",
          type: "attribute",
          attribute: "href",
          all: true
        }
      }
    }
  }),
});
const result = await response.json();
console.log(result.analysis);
Python
import requests

response = requests.post(
    "https://api.domscout.io/scrape",
    headers={"x-api-key": "YOUR_API_KEY"},
    json={
        "url": "https://www.example.com/en/",
        "extractMarkdown": False,
        "extract": {
            "fields": {
                "languages": {
                    "selector": "link[rel='alternate'][hreflang]",
                    "type": "attribute",
                    "attribute": "hreflang",
                    "all": True
                },
                "urls": {
                    "selector": "link[rel='alternate'][hreflang]",
                    "type": "attribute",
                    "attribute": "href",
                    "all": True
                }
            }
        }
    },
)
print(response.json()["analysis"])
Go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"url": "https://www.example.com/en/",
		"extractMarkdown": false,
		"extract": map[string]any{
			"fields": map[string]any{
				"languages": map[string]any{
					"selector": "link[rel='alternate'][hreflang]",
					"type": "attribute",
					"attribute": "hreflang",
					"all": true,
				},
				"urls": map[string]any{
					"selector": "link[rel='alternate'][hreflang]",
					"type": "attribute",
					"attribute": "href",
					"all": true,
				},
			},
		},
	})
	req, _ := http.NewRequest("POST", "https://api.domscout.io/scrape", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", "YOUR_API_KEY")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result["analysis"])
}
PHP
<?php
$ch = curl_init('https://api.domscout.io/scrape');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'x-api-key: YOUR_API_KEY'],
    CURLOPT_POSTFIELDS => json_encode([
        'url' => 'https://www.example.com/en/',
        'extractMarkdown' => false,
        'extract' => [
            'fields' => [
                'languages' => [
                    'selector' => 'link[rel=\'alternate\'][hreflang]',
                    'type' => 'attribute',
                    'attribute' => 'hreflang',
                    'all' => true
                ],
                'urls' => [
                    'selector' => 'link[rel=\'alternate\'][hreflang]',
                    'type' => 'attribute',
                    'attribute' => 'href',
                    'all' => true
                ]
            ]
        ]
    ]),
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
$result = json_decode($response, true);
print_r($result['analysis']);
Ruby
require "json"
require "net/http"

response = Net::HTTP.post(
  URI("https://api.domscout.io/scrape"),
  {
    url: "https://www.example.com/en/",
    extractMarkdown: false,
    extract: {
      fields: {
        languages: {
          selector: "link[rel='alternate'][hreflang]",
          type: "attribute",
          attribute: "hreflang",
          all: true
        },
        urls: {
          selector: "link[rel='alternate'][hreflang]",
          type: "attribute",
          attribute: "href",
          all: true
        }
      }
    }
  }.to_json,
  "Content-Type" => "application/json",
  "x-api-key" => "YOUR_API_KEY"
)
puts JSON.parse(response.body)["analysis"]
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Capture {
    public static void main(String[] args) throws Exception {
        String body = """
            {
                "url": "https://www.example.com/en/",
                "extractMarkdown": false,
                "extract": {
                    "fields": {
                        "languages": {
                            "selector": "link[rel='alternate'][hreflang]",
                            "type": "attribute",
                            "attribute": "hreflang",
                            "all": true
                        },
                        "urls": {
                            "selector": "link[rel='alternate'][hreflang]",
                            "type": "attribute",
                            "attribute": "href",
                            "all": true
                        }
                    }
                }
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.domscout.io/scrape"))
            .header("Content-Type", "application/json")
            .header("x-api-key", "YOUR_API_KEY")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
C#
using System.Net.Http.Json;
using System.Text.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");

var response = await client.PostAsJsonAsync("https://api.domscout.io/scrape", new {
    url = "https://www.example.com/en/",
    extractMarkdown = false,
    extract = new {
        fields = new {
            languages = new {
                selector = "link[rel='alternate'][hreflang]",
                type = "attribute",
                attribute = "hreflang",
                all = true
            },
            urls = new {
                selector = "link[rel='alternate'][hreflang]",
                type = "attribute",
                attribute = "href",
                all = true
            }
        }
    }
});
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(result.GetProperty("analysis"));

Go further

More in SEO and site health: SEO regression checks · Link preview checks · Find every page on a site · Website migration checks · JavaScript SEO checks · Page weight and load metrics · Backlink checks · JSON-LD structured data checks · Website audits for prospects · All use cases

Give your product a browser.

Get clean web content and visual proof into your workflow in minutes.