ChartQuery

Analyze a Page with AI

Send a URL to the CaptureKit AI analysis endpoint and receive structured insights about the page's content, design, technology stack, and SEO metadata.

Overview

The CaptureKit AI analysis endpoint combines screenshot capture with LLM-powered analysis. Pass any URL and a custom prompt — the API returns a structured JSON answer grounded in what the page actually looks like. Use it for competitor audits, UX reviews, or automated content QA.

Prerequisites

  • A CaptureKit API key — get one at app.capturekit.dev
  • Install dependencies for your language:
pip install requests

No extra dependencies — uses the native fetch API (Node 18+).

curl extension enabled (on by default).

No extra dependencies — uses net/http (Go 1.18+).

No extra dependencies — uses java.net.http (Java 11+).

No extra dependencies — uses System.Net.Http (.NET 6+).

# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"

Steps

Submit an analysis request

Call GET /v1/analyze with the url and prompt parameters. The prompt shapes the AI's response.

import requests

API_KEY = "YOUR_API_KEY"
PROMPT  = "Summarize the main value proposition of this page in 2 sentences. Then list the top 3 CTAs visible above the fold."

response = requests.get(
    "https://api.capturekit.dev/v1/analyze",
    headers={"x-api-key": API_KEY},
    params={"url": "https://stripe.com", "prompt": PROMPT},
)
data = response.json()
print(data)
const API_KEY = "YOUR_API_KEY";
const PROMPT  = "Summarize the main value proposition of this page in 2 sentences. Then list the top 3 CTAs visible above the fold.";

const params   = new URLSearchParams({ url: "https://stripe.com", prompt: PROMPT });
const response = await fetch(`https://api.capturekit.dev/v1/analyze?${params}`, {
  headers: { "x-api-key": API_KEY },
});
const data = await response.json();
console.log(data);
<?php
$apiKey = "YOUR_API_KEY";
$prompt = "Summarize the main value proposition of this page in 2 sentences. Then list the top 3 CTAs visible above the fold.";
$params = http_build_query(["url" => "https://stripe.com", "prompt" => $prompt]);

$ch = curl_init("https://api.capturekit.dev/v1/analyze?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    params := url.Values{
        "url":    {"https://stripe.com"},
        "prompt": {"Summarize the main value proposition in 2 sentences. List the top 3 CTAs above the fold."},
    }
    req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/analyze?"+params.Encode(), nil)
    req.Header.Set("x-api-key", "YOUR_API_KEY")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)

    var data map[string]any
    json.Unmarshal(body, &data)
    fmt.Println(data)
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;

var client  = HttpClient.newHttpClient();
var prompt  = URLEncoder.encode("Summarize the value proposition in 2 sentences. List the top 3 CTAs above the fold.", StandardCharsets.UTF_8);
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.capturekit.dev/v1/analyze?url=https%3A%2F%2Fstripe.com&prompt=" + prompt))
    .header("x-api-key", "YOUR_API_KEY").GET().build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
using System.Net.Http;

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

var prompt = Uri.EscapeDataString("Summarize the value proposition in 2 sentences. List the top 3 CTAs above the fold.");
var body   = await client.GetStringAsync(
    $"https://api.capturekit.dev/v1/analyze?url=https%3A%2F%2Fstripe.com&prompt={prompt}");
Console.WriteLine(body);
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let data = reqwest::Client::new()
        .get("https://api.capturekit.dev/v1/analyze")
        .header("x-api-key", "YOUR_API_KEY")
        .query(&[
            ("url",    "https://stripe.com"),
            ("prompt", "Summarize the value proposition in 2 sentences. List the top 3 CTAs above the fold."),
        ])
        .send().await?.json::<serde_json::Value>().await?;

    println!("{:#?}", data);
    Ok(())
}

Read the AI analysis

The response includes analysis (the AI's answer to your prompt), screenshot_url, and metadata like title and description.

print(f"Page    : {data.get('title')}")
print(f"Preview : {data.get('screenshot_url')}")
print()
print("=== AI Analysis ===")
print(data.get("analysis", ""))
console.log(`Page    : ${data.title}`);
console.log(`Preview : ${data.screenshot_url}\n`);
console.log("=== AI Analysis ===");
console.log(data.analysis);
echo "Page    : {$data['title']}\n";
echo "Preview : {$data['screenshot_url']}\n\n";
echo "=== AI Analysis ===\n";
echo $data["analysis"] ?? "";
fmt.Printf("Page    : %v\nPreview : %v\n\n=== AI Analysis ===\n%v\n",
    data["title"], data["screenshot_url"], data["analysis"])
import org.json.*;

var d = new JSONObject(response.body());
System.out.printf("Page    : %s%nPreview : %s%n%n=== AI Analysis ===%n%s%n",
    d.getString("title"), d.getString("screenshot_url"), d.getString("analysis"));
using System.Text.Json;

var d = JsonDocument.Parse(body).RootElement;
Console.WriteLine($"Page    : {d.GetProperty("title")}");
Console.WriteLine($"Preview : {d.GetProperty("screenshot_url")}\n");
Console.WriteLine("=== AI Analysis ===");
Console.WriteLine(d.GetProperty("analysis"));
println!("Page    : {}", data["title"].as_str().unwrap_or(""));
println!("Preview : {}\n", data["screenshot_url"].as_str().unwrap_or(""));
println!("=== AI Analysis ===");
println!("{}", data["analysis"].as_str().unwrap_or(""));

Run a competitor audit across multiple pages

Analyze several competitor landing pages with a consistent prompt and save the results as a structured report.

import json, time, requests

API_KEY = "YOUR_API_KEY"
PROMPT  = """Analyze this landing page and return a JSON object with these keys:
- value_proposition (string)
- primary_cta (string)
- target_audience (string)
- pricing_visible (boolean)
- social_proof_types (array of strings: "testimonials", "logos", "stats", etc.)"""

COMPETITORS = [
    {"name": "Stripe",  "url": "https://stripe.com"},
    {"name": "Paddle",  "url": "https://paddle.com"},
    {"name": "Lemonsqueezy", "url": "https://lemonsqueezy.com"},
]
report = []

for comp in COMPETITORS:
    r    = requests.get("https://api.capturekit.dev/v1/analyze",
        headers={"x-api-key": API_KEY},
        params={"url": comp["url"], "prompt": PROMPT})
    data = r.json()
    analysis_text = data.get("analysis", "{}")
    try:
        # AI may return the JSON wrapped in markdown fences
        raw = analysis_text.strip().removeprefix("```json").removesuffix("```").strip()
        analysis = json.loads(raw)
    except json.JSONDecodeError:
        analysis = {"raw": analysis_text}

    report.append({"competitor": comp["name"], "url": comp["url"], **analysis})
    print(f"✓ {comp['name']} analyzed")
    time.sleep(2)

with open("competitor_audit.json", "w") as f:
    json.dump(report, f, indent=2)
print("\nReport saved to competitor_audit.json")
import { writeFileSync } from "fs";

const API_KEY = "YOUR_API_KEY";
const PROMPT  = `Analyze this landing page and return a JSON object with these keys:
- value_proposition (string)
- primary_cta (string)
- target_audience (string)
- pricing_visible (boolean)
- social_proof_types (array of strings)`;

const competitors = [
  { name: "Stripe",       url: "https://stripe.com" },
  { name: "Paddle",       url: "https://paddle.com" },
  { name: "Lemonsqueezy", url: "https://lemonsqueezy.com" },
];
const report: any[] = [];

for (const comp of competitors) {
  const params = new URLSearchParams({ url: comp.url, prompt: PROMPT });
  const res    = await fetch(`https://api.capturekit.dev/v1/analyze?${params}`, {
    headers: { "x-api-key": API_KEY },
  });
  const data   = await res.json();
  let analysis: any;
  try {
    const raw = (data.analysis ?? "{}").replace(/^```json\n?/, "").replace(/```$/, "").trim();
    analysis  = JSON.parse(raw);
  } catch { analysis = { raw: data.analysis }; }

  report.push({ competitor: comp.name, url: comp.url, ...analysis });
  console.log(`✓ ${comp.name} analyzed`);
  await new Promise(r => setTimeout(r, 2000));
}

writeFileSync("competitor_audit.json", JSON.stringify(report, null, 2));
console.log("\nReport saved to competitor_audit.json");
<?php
$apiKey      = "YOUR_API_KEY";
$prompt      = "Analyze this landing page and return a JSON object with keys: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";
$competitors = [
    ["name" => "Stripe",       "url" => "https://stripe.com"],
    ["name" => "Paddle",       "url" => "https://paddle.com"],
    ["name" => "Lemonsqueezy", "url" => "https://lemonsqueezy.com"],
];
$report = [];

foreach ($competitors as $comp) {
    $params = http_build_query(["url" => $comp["url"], "prompt" => $prompt]);
    $ch = curl_init("https://api.capturekit.dev/v1/analyze?{$params}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
    $data = json_decode(curl_exec($ch), true);
    curl_close($ch);

    $raw      = preg_replace('/^```json\n?|```$/', '', trim($data["analysis"] ?? "{}"));
    $analysis = json_decode($raw, true) ?? ["raw" => $data["analysis"]];
    $report[] = array_merge(["competitor" => $comp["name"], "url" => $comp["url"]], $analysis);
    echo "✓ {$comp['name']} analyzed\n";
    sleep(2);
}

file_put_contents("competitor_audit.json", json_encode($report, JSON_PRETTY_PRINT));
echo "\nReport saved to competitor_audit.json\n";
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strings"
    "time"
)

const (
    APIKey = "YOUR_API_KEY"
    Prompt = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types."
)

type Competitor struct{ Name, URL string }

func analyze(comp Competitor) map[string]any {
    params := url.Values{"url": {comp.URL}, "prompt": {Prompt}}
    req, _ := http.NewRequest("GET", "https://api.capturekit.dev/v1/analyze?"+params.Encode(), nil)
    req.Header.Set("x-api-key", APIKey)
    resp, _ := http.DefaultClient.Do(req)
    body, _ := io.ReadAll(resp.Body)
    resp.Body.Close()

    var data map[string]any
    json.Unmarshal(body, &data)

    raw := strings.TrimSpace(fmt.Sprint(data["analysis"]))
    raw  = strings.TrimPrefix(raw, "```json")
    raw  = strings.TrimSuffix(raw, "```")
    var analysis map[string]any
    if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &analysis); err != nil {
        analysis = map[string]any{"raw": raw}
    }
    analysis["competitor"] = comp.Name
    analysis["url"]        = comp.URL
    return analysis
}

func main() {
    competitors := []Competitor{
        {"Stripe", "https://stripe.com"},
        {"Paddle", "https://paddle.com"},
        {"Lemonsqueezy", "https://lemonsqueezy.com"},
    }
    var report []map[string]any
    for _, c := range competitors {
        report = append(report, analyze(c))
        fmt.Printf("✓ %s analyzed\n", c.Name)
        time.Sleep(2 * time.Second)
    }
    b, _ := json.MarshalIndent(report, "", "  ")
    os.WriteFile("competitor_audit.json", b, 0644)
    fmt.Println("\nReport saved to competitor_audit.json")
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import org.json.*;

public class Main {
    static final String API_KEY = "YOUR_API_KEY";
    static final String PROMPT  = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";

    public static void main(String[] args) throws Exception {
        var client      = HttpClient.newHttpClient();
        var competitors = List.of(
            Map.of("name","Stripe",       "url","https://stripe.com"),
            Map.of("name","Paddle",       "url","https://paddle.com"),
            Map.of("name","Lemonsqueezy", "url","https://lemonsqueezy.com"));
        var report = new JSONArray();

        for (var comp : competitors) {
            var encodedUrl    = URLEncoder.encode(comp.get("url"), StandardCharsets.UTF_8);
            var encodedPrompt = URLEncoder.encode(PROMPT, StandardCharsets.UTF_8);
            var req  = HttpRequest.newBuilder()
                .uri(URI.create("https://api.capturekit.dev/v1/analyze?url=" + encodedUrl + "&prompt=" + encodedPrompt))
                .header("x-api-key", API_KEY).GET().build();
            var resp      = client.send(req, HttpResponse.BodyHandlers.ofString());
            var data      = new JSONObject(resp.body());
            var analysisText = data.getString("analysis")
                .replaceAll("^```json\\n?","").replaceAll("```$","").strip();
            JSONObject analysis;
            try { analysis = new JSONObject(analysisText); }
            catch (Exception e) { analysis = new JSONObject().put("raw", analysisText); }
            analysis.put("competitor", comp.get("name")).put("url", comp.get("url"));
            report.put(analysis);
            System.out.println("✓ " + comp.get("name") + " analyzed");
            Thread.sleep(2000);
        }
        Files.writeString(Path.of("competitor_audit.json"), report.toString(2));
        System.out.println("\nReport saved to competitor_audit.json");
    }
}
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;

const string API_KEY = "YOUR_API_KEY";
const string PROMPT  = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";

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

var competitors = new[] {
    (name: "Stripe",       url: "https://stripe.com"),
    (name: "Paddle",       url: "https://paddle.com"),
    (name: "Lemonsqueezy", url: "https://lemonsqueezy.com"),
};
var report = new List<object>();

foreach (var comp in competitors)
{
    var encodedUrl    = Uri.EscapeDataString(comp.url);
    var encodedPrompt = Uri.EscapeDataString(PROMPT);
    var body          = await client.GetStringAsync($"https://api.capturekit.dev/v1/analyze?url={encodedUrl}&prompt={encodedPrompt}");
    var data          = JsonDocument.Parse(body).RootElement;
    var analysisText  = data.GetProperty("analysis").GetString() ?? "{}";
    var clean         = Regex.Replace(analysisText.Trim(), @"^```json\n?|```$", "").Trim();
    object analysis;
    try   { analysis = JsonSerializer.Deserialize<Dictionary<string, object>>(clean)!; }
    catch { analysis = new { raw = analysisText }; }
    report.Add(new { competitor = comp.name, comp.url, analysis });
    Console.WriteLine($"✓ {comp.name} analyzed");
    await Task.Delay(2000);
}

File.WriteAllText("competitor_audit.json",
    JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true }));
Console.WriteLine("\nReport saved to competitor_audit.json");
use reqwest::Client;
use serde_json::{json, Value};
use std::{fs, time::Duration};
use tokio::time::sleep;

const API_KEY: &str = "YOUR_API_KEY";
const PROMPT: &str  = "Analyze this landing page and return a JSON object: value_proposition, primary_cta, target_audience, pricing_visible, social_proof_types.";

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client      = Client::new();
    let competitors = [("Stripe","https://stripe.com"), ("Paddle","https://paddle.com"), ("Lemonsqueezy","https://lemonsqueezy.com")];
    let mut report  = Vec::new();

    for (name, url) in &competitors {
        let data = client.get("https://api.capturekit.dev/v1/analyze")
            .header("x-api-key", API_KEY)
            .query(&[("url", url), ("prompt", &PROMPT)])
            .send().await?.json::<Value>().await?;

        let analysis_text = data["analysis"].as_str().unwrap_or("{}");
        let clean         = analysis_text.trim().trim_start_matches("```json").trim_end_matches("```").trim();
        let analysis: Value = serde_json::from_str(clean).unwrap_or(json!({ "raw": analysis_text }));

        report.push(json!({ "competitor": name, "url": url, "analysis": analysis }));
        println!("✓ {} analyzed", name);
        sleep(Duration::from_secs(2)).await;
    }

    fs::write("competitor_audit.json", serde_json::to_string_pretty(&report).unwrap()).unwrap();
    println!("\nReport saved to competitor_audit.json");
    Ok(())
}

Instruct the AI to return structured JSON in your prompt — the model will format its output accordingly, making it easy to parse and store results in a database or spreadsheet without additional post-processing.

On this page