Skip to content

Troubleshooting

When an Insights result looks wrong, the cause is usually one of four things: the page rendered as a generic shell, the request did too much work, the wrong page variant was analyzed, or the site blocked browser automation.

Quick triage checklist

  1. Run one analysis at a time before you run both.
  2. If the result is missing or generic on a SPA, try prerender: true.
  3. If the request is too slow, remove fixed waits and unnecessary analysis first.
  4. If the wrong locale, region, or personalization is showing up, use headers or forwarded auth headers
    PRO
    .
  5. If the site blocks automation or geofences content, use proxy with a proxy URL
    PRO
    .

The result is missing or generic

Start by forcing browser rendering:

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://vercel.com' URL with 'insights', 'meta' & 'prerender' API parameters:

CLI Microlink API example

microlink https://vercel.com&insights.technologies&prerender

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://vercel.com" \
  -d "insights.technologies=true" \
  -d "insights.lighthouse=false" \
  -d "meta=false" \
  -d "prerender=true"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://vercel.com', {
  insights: {
    technologies: true,
    lighthouse: false
  },
  meta: false,
  prerender: true
})

Python Microlink API example

import requests

url = "https://api.microlink.io/"

querystring = {
    "url": "https://vercel.com",
    "insights.technologies": "true",
    "insights.lighthouse": "false",
    "meta": "false",
    "prerender": "true"
}

response = requests.get(url, params=querystring)

print(response.json())

Ruby Microlink API example

require 'uri'
require 'net/http'

base_url = "https://api.microlink.io/"

params = {
  url: "https://vercel.com",
  insights.technologies: "true",
  insights.lighthouse: "false",
  meta: "false",
  prerender: "true"
}

uri = URI(base_url)
uri.query = URI.encode_www_form(params)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
response = http.request(request)

puts response.body

PHP Microlink API example

<?php

$baseUrl = "https://api.microlink.io/";

$params = [
    "url" => "https://vercel.com",
    "insights.technologies" => "true",
    "insights.lighthouse" => "false",
    "meta" => "false",
    "prerender" => "true"
];

$query = http_build_query($params);
$url = $baseUrl . '?' . $query;

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET"
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
    echo "cURL Error #: " . $err;
} else {
    echo $response;
}

Golang Microlink API example

package main

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

func main() {
    baseURL := "https://api.microlink.io"

    u, err := url.Parse(baseURL)
    if err != nil {
        panic(err)
    }
    q := u.Query()
    q.Set("url", "https://vercel.com")
    q.Set("insights.technologies", "true")
    q.Set("insights.lighthouse", "false")
    q.Set("meta", "false")
    q.Set("prerender", "true")
    u.RawQuery = q.Encode()

    req, err := http.NewRequest("GET", u.String(), nil)
    if err != nil {
        panic(err)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
}
When the initial HTML is too generic, prerender: true can expose the real page state before analysis runs.
If the output is still wrong, switch to a single analysis and add waitUntil plus waitForSelector for dynamic pages.

The request is too slow or times out

If you hit ETIMEOUT or EBRWSRTIMEOUT, reduce the amount of work before you simply raise timeout:

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://vercel.com' URL with 'insights', 'meta', 'retry' & 'timeout' API parameters:

CLI Microlink API example

microlink https://vercel.com&insights.lighthouse&retry=3&timeout=20s

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://vercel.com" \
  -d "insights.technologies=false" \
  -d "insights.lighthouse=true" \
  -d "meta=false" \
  -d "retry=3" \
  -d "timeout=20s"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://vercel.com', {
  insights: {
    technologies: false,
    lighthouse: true
  },
  meta: false,
  retry: 3,
  timeout: "20s"
})

Python Microlink API example

import requests

url = "https://api.microlink.io/"

querystring = {
    "url": "https://vercel.com",
    "insights.technologies": "false",
    "insights.lighthouse": "true",
    "meta": "false",
    "retry": "3",
    "timeout": "20s"
}

response = requests.get(url, params=querystring)

print(response.json())

Ruby Microlink API example

require 'uri'
require 'net/http'

base_url = "https://api.microlink.io/"

params = {
  url: "https://vercel.com",
  insights.technologies: "false",
  insights.lighthouse: "true",
  meta: "false",
  retry: "3",
  timeout: "20s"
}

uri = URI(base_url)
uri.query = URI.encode_www_form(params)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
response = http.request(request)

puts response.body

PHP Microlink API example

<?php

$baseUrl = "https://api.microlink.io/";

$params = [
    "url" => "https://vercel.com",
    "insights.technologies" => "false",
    "insights.lighthouse" => "true",
    "meta" => "false",
    "retry" => "3",
    "timeout" => "20s"
];

$query = http_build_query($params);
$url = $baseUrl . '?' . $query;

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET"
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
    echo "cURL Error #: " . $err;
} else {
    echo $response;
}

Golang Microlink API example

package main

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

func main() {
    baseURL := "https://api.microlink.io"

    u, err := url.Parse(baseURL)
    if err != nil {
        panic(err)
    }
    q := u.Query()
    q.Set("url", "https://vercel.com")
    q.Set("insights.technologies", "false")
    q.Set("insights.lighthouse", "true")
    q.Set("meta", "false")
    q.Set("retry", "3")
    q.Set("timeout", "20s")
    u.RawQuery = q.Encode()

    req, err := http.NewRequest("GET", u.String(), nil)
    if err != nil {
        panic(err)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
}
Increase timeout only after removing unnecessary analysis and rendering work.
The most effective fixes are:
  • run only technologies or only Lighthouse, not both
  • set meta: false when the standard metadata payload is not needed
  • use filter: 'insights' when you only need the Insights payload
  • use prerender: false if the page already exposes enough information in HTML
  • replace waitForTimeout with waitForSelector
  • disable javascript when the page does not need it
  • add ttl or staleTtl
    PRO
    for repeated runs

Private pages and blocked sites
PRO

If the page only works for logged-in users, a specific locale, or a particular request context:
  • use headers for non-sensitive request shaping
  • use x-api-header-* for cookies or authorization
  • use pro.microlink.io when sending x-api-key
Some sites block headless browsers, require a region-specific IP, or trigger antibot protection. In those cases, use proxy:

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://example.com' URL with 'insights', 'meta' & 'proxy' API parameters:

CLI Microlink API example

microlink https://example.com&insights.technologies&proxy=https://myproxy:[email protected]:8001

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://example.com" \
  -d "insights.technologies=true" \
  -d "insights.lighthouse=false" \
  -d "meta=false" \
  -d "proxy=https://myproxy:[email protected]:8001"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://example.com', {
  insights: {
    technologies: true,
    lighthouse: false
  },
  meta: false,
  proxy: "https://myproxy:[email protected]:8001"
})

Python Microlink API example

import requests

url = "https://api.microlink.io/"

querystring = {
    "url": "https://example.com",
    "insights.technologies": "true",
    "insights.lighthouse": "false",
    "meta": "false",
    "proxy": "https://myproxy:[email protected]:8001"
}

response = requests.get(url, params=querystring)

print(response.json())

Ruby Microlink API example

require 'uri'
require 'net/http'

base_url = "https://api.microlink.io/"

params = {
  url: "https://example.com",
  insights.technologies: "true",
  insights.lighthouse: "false",
  meta: "false",
  proxy: "https://myproxy:[email protected]:8001"
}

uri = URI(base_url)
uri.query = URI.encode_www_form(params)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
response = http.request(request)

puts response.body

PHP Microlink API example

<?php

$baseUrl = "https://api.microlink.io/";

$params = [
    "url" => "https://example.com",
    "insights.technologies" => "true",
    "insights.lighthouse" => "false",
    "meta" => "false",
    "proxy" => "https://myproxy:[email protected]:8001"
];

$query = http_build_query($params);
$url = $baseUrl . '?' . $query;

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET"
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
    echo "cURL Error #: " . $err;
} else {
    echo $response;
}

Golang Microlink API example

package main

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

func main() {
    baseURL := "https://api.microlink.io"

    u, err := url.Parse(baseURL)
    if err != nil {
        panic(err)
    }
    q := u.Query()
    q.Set("url", "https://example.com")
    q.Set("insights.technologies", "true")
    q.Set("insights.lighthouse", "false")
    q.Set("meta", "false")
    q.Set("proxy", "https://myproxy:[email protected]:8001")
    u.RawQuery = q.Encode()

    req, err := http.NewRequest("GET", u.String(), nil)
    if err != nil {
        panic(err)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
}
Use a proxy URL when the target site blocks headless traffic, geofences content, or rate-limits your origin.
If the API returns EPROXYNEEDED, that is the clearest signal that the target site needs a proxy-backed request.

Auth and plan errors

Some common errors point directly to setup issues:
  • EAUTH — the API key is missing or invalid.
  • EPRO — you sent x-api-key to api.microlink.io instead of pro.microlink.io.
  • EHEADERSheaders requires a
    PRO
    plan.
  • EPROXYproxy requires a
    PRO
    plan.
  • ETTL or ESTTL — configurable cache parameters require a
    PRO
    plan.
  • ERATE — you reached the free-tier or plan quota limit.

Useful headers while debugging

Open the response headers view in the interactive editor and look for:
  • x-cache-status — whether the response was a MISS, HIT, or BYPASS
  • x-cache-ttl — the effective cache lifetime
  • x-fetch-mode — whether the request was fetched, prerendered, or proxy-backed
  • x-fetch-time — time spent fetching and rendering
  • x-pricing-plan — whether the request ran on the free or Pro plan
  • x-response-time — the total request duration
These headers usually tell you whether the problem is caching, rendering, auth, or target-site protection.

Next step

See the guides overview for the rest of the Microlink guide set.