NewIntroducing semantic snapshotsPair every capture with structured DOM data →

For teams working on accessibility

Find the buttons and links a screen reader cannot name

List every control on the rendered page with the name a screen reader would announce, and find the ones that have none.

The problem

An icon-only button with no label is announced as "button" and nothing more. It looks fine to every sighted reviewer, passes visual tests, and turns up in exactly the components that get reused most: close buttons, menu toggles, social links.

How domscout handles it

semanticNodes: true returns interactiveNodes, every interactive element on the rendered page with its role and the name assistive technology would announce, resolved the way a screen reader resolves it: visible text, then aria-label, aria-labelledby, value, title, placeholder and alt. nameSource says which one supplied the name, and an element with empty text and a nameSource of null has no accessible name at all. This is one check, not an audit: it does not test colour contrast, keyboard order or anything a person has to judge.

Where it shows up

  • A CI check that no unlabelled controls ship on key pages.
  • A sweep of a component library's demo pages.
  • A list of fixes for the team that owns each template.

The request

POST /screenshot. Available on every plan, including the free Hobby plan, at 1 credit a page. To check focus styling as well, forceState can hold an element in its focus state for the screenshot.

cURL
curl -X POST https://api.domscout.io/screenshot \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.example.com/",
    "semanticNodes": true,
    "responseType": "json"
  }'
Node.js
const response = await fetch("https://api.domscout.io/screenshot", {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": "YOUR_API_KEY" },
  body: JSON.stringify({
    url: "https://www.example.com/",
    semanticNodes: true,
    responseType: "json"
  }),
});
const result = await response.json();
console.log(result.interactiveNodes);
Python
import requests

response = requests.post(
    "https://api.domscout.io/screenshot",
    headers={"x-api-key": "YOUR_API_KEY"},
    json={
        "url": "https://www.example.com/",
        "semanticNodes": True,
        "responseType": "json"
    },
)
print(response.json()["interactiveNodes"])
Go
package main

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

func main() {
	body, _ := json.Marshal(map[string]any{
		"url": "https://www.example.com/",
		"semanticNodes": true,
		"responseType": "json",
	})
	req, _ := http.NewRequest("POST", "https://api.domscout.io/screenshot", 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["interactiveNodes"])
}
PHP
<?php
$ch = curl_init('https://api.domscout.io/screenshot');
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/',
        'semanticNodes' => true,
        'responseType' => 'json'
    ]),
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
$result = json_decode($response, true);
print_r($result['interactiveNodes']);
Ruby
require "json"
require "net/http"

response = Net::HTTP.post(
  URI("https://api.domscout.io/screenshot"),
  {
    url: "https://www.example.com/",
    semanticNodes: true,
    responseType: "json"
  }.to_json,
  "Content-Type" => "application/json",
  "x-api-key" => "YOUR_API_KEY"
)
puts JSON.parse(response.body)["interactiveNodes"]
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/",
                "semanticNodes": true,
                "responseType": "json"
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.domscout.io/screenshot"))
            .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/screenshot", new {
    url = "https://www.example.com/",
    semanticNodes = true,
    responseType = "json"
});
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(result.GetProperty("interactiveNodes"));

Go further

More in Testing and QA: Responsive layout checks · Visual regression testing · JavaScript error checks · Signup and checkout flow checks · Hover, focus and dark mode screenshots · Localization screenshots · A screenshot of every step · Embedded widget checks · Screenshots for support tickets · Ad landing page checks · Before and after hydration · Selectors for test automation · Print layout checks · All use cases

Give your product a browser.

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