Troubleshooting
When custom extraction looks wrong, the cause is usually one of five things: the rule is targeting the wrong element, the page was not ready yet, the wrong page variant was rendered, the request timed out, or the site blocked the request.
For timeouts, blocked sites, auth/plan errors, and debug headers that apply to all workflows, see common troubleshooting.
A field is empty or null
Start by tightening the rule and adding fallbacks:
The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://example.com' URL with 'data' & 'meta' API parameters:
CLI Microlink API example
microlink https://example.com&data.title='[object Object],[object Object],[object Object]'cURL Microlink API example
curl -G "https://api.microlink.io" \
-d "url=https://example.com" \
-d "data.title=%5Bobject%20Object%5D%2C%5Bobject%20Object%5D%2C%5Bobject%20Object%5D" \
-d "meta=false"JavaScript Microlink API example
import mql from '@microlink/mql'
const { data } = await mql('https://example.com', {
data: {
title: [
{
selector: 'meta[property="og:title"]',
attr: "content"
},
{
selector: "title",
attr: "text"
},
{
selector: "h1",
attr: "text"
}
]
},
meta: false
})Python Microlink API example
import requests
url = "https://api.microlink.io/"
querystring = {
"url": "https://example.com",
"data.title": "[object Object],[object Object],[object Object]",
"meta": "false"
}
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",
data.title: "[object Object],[object Object],[object Object]",
meta: "false"
}
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.bodyPHP Microlink API example
<?php
$baseUrl = "https://api.microlink.io/";
$params = [
"url" => "https://example.com",
"data.title" => "[object Object],[object Object],[object Object]",
"meta" => "false"
];
$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("data.title", "[object Object],[object Object],[object Object]")
q.Set("meta", "false")
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))
}import mql from '@microlink/mql'
const { data } = await mql('https://example.com', {
data: {
title: [
{
selector: 'meta[property="og:title"]',
attr: "content"
},
{
selector: "title",
attr: "text"
},
{
selector: "h1",
attr: "text"
}
]
},
meta: false
})Try the most specific selector first, then fall back to broader rules only when needed.
If the field is still empty:
- Check whether the selector exists in the rendered DOM.
- Remove
typetemporarily to see whether validation is discarding the value. - Confirm you are extracting the right page variant for that device, locale, or auth state.
A collection or object has the wrong shape
The most common rule-shape mistakes are:
- Using
selectorwhen you really needselectorAll. - Returning a single string when you meant to build an object with nested
attrrules. - Building a nested object too early before the individual child selectors work.
Fix the smallest piece first, then rebuild the larger structure around it.
The page is not ready yet
Dynamic pages often need a selector-based wait:
The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://dev.to' URL with 'data', 'meta', 'waitUntil' & 'waitForSelector' API parameters:
CLI Microlink API example
microlink https://dev.to&data.title.selector='main h1'&data.title.attr=text&waitUntil=domcontentloaded&waitForSelector='main h1'cURL Microlink API example
curl -G "https://api.microlink.io" \
-d "url=https://dev.to" \
-d "data.title.selector=main%20h1" \
-d "data.title.attr=text" \
-d "meta=false" \
-d "waitUntil=domcontentloaded" \
-d "waitForSelector=main%20h1"JavaScript Microlink API example
import mql from '@microlink/mql'
const { data } = await mql('https://dev.to', {
data: {
title: {
selector: "main h1",
attr: "text"
}
},
meta: false,
waitUntil: "domcontentloaded",
waitForSelector: "main h1"
})Python Microlink API example
import requests
url = "https://api.microlink.io/"
querystring = {
"url": "https://dev.to",
"data.title.selector": "main h1",
"data.title.attr": "text",
"meta": "false",
"waitUntil": "domcontentloaded",
"waitForSelector": "main h1"
}
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://dev.to",
data.title.selector: "main h1",
data.title.attr: "text",
meta: "false",
waitUntil: "domcontentloaded",
waitForSelector: "main h1"
}
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.bodyPHP Microlink API example
<?php
$baseUrl = "https://api.microlink.io/";
$params = [
"url" => "https://dev.to",
"data.title.selector" => "main h1",
"data.title.attr" => "text",
"meta" => "false",
"waitUntil" => "domcontentloaded",
"waitForSelector" => "main h1"
];
$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://dev.to")
q.Set("data.title.selector", "main h1")
q.Set("data.title.attr", "text")
q.Set("meta", "false")
q.Set("waitUntil", "domcontentloaded")
q.Set("waitForSelector", "main h1")
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))
}import mql from '@microlink/mql'
const { data } = await mql('https://dev.to', {
data: {
title: {
selector: "main h1",
attr: "text"
}
},
meta: false,
waitUntil: "domcontentloaded",
waitForSelector: "main h1"
})This is usually more reliable than waiting a fixed number of seconds.
If the field still comes back empty, force browser rendering with
prerender: true.Function errors
EINVALFUNCTION— thefunctionparameter has invalid JavaScript syntax.EINVALEVAL— the custom extraction logic failed during evaluation.
Still stuck
Check the full error codes reference or see common troubleshooting for timeout, auth, and plan errors. If the issue is specific to authenticated targets, return to private pages.
Back to guides
See the guides overview for more Microlink guides.