Extract every link and email address from a web page
To extract all links from a website page you need absolute URLs, not a mix of relative paths, anchors and javascript: handlers. Lead enrichment, link audits, partner directories, crawl seeds and contact pages all start with the same two lists. The Scraping API returns them with one method each, already cleaned.
Raw hrefs and mailto links need cleaning before they are data
A page’s anchors are a mess of relative paths, fragment links, duplicates, tracking redirects, mailto: and javascript: pseudo-links. Before you can store or follow them, each one needs resolving against the page URL, validating and deduplicating.
Email addresses are worse: some sit in mailto links, others only in plain text in a footer or a contact paragraph. A regular expression over the HTML catches addresses inside scripts and misses the ones behind a mailto prefix, and a plain fetch sees nothing at all on contact pages rendered in the browser.
The links method sweeps every anchor and returns absolute, deduplicated URLs, dropping mailto, javascript and empty hrefs. The emails method scans mailto links and plain text and returns bare addresses with the mailto prefix stripped. Both accept a selector to scope the sweep and the shared options to render first.
How to get all URLs and emails from a page
Each method is one request that resolves to an array of strings. Scope it, render it, or drop down to a rule when you need more than the URL.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const url = 'https://example.com/contact'
const [links, emails] = await Promise.all([
microlink.links(url),
microlink.emails(url)
])links resolves to absolute, deduplicated URLs and emails to addresses such as [email protected]. The two calls run in parallel and each is cached on its own.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const nav = await microlink.links('https://example.com', {
selectorAll: 'nav a'
})
const footer = await microlink.emails('https://example.com', {
selector: 'footer'
})
const { hostname } = new URL('https://example.com')
const external = nav.filter(link => new URL(link).hostname !== hostname)selectorAll narrows links to the navigation and selector narrows emails to the footer. Filtering by hostname on your side splits internal from external links.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const { anchors } = await microlink.extract('https://example.com', {
anchors: {
selectorAll: 'main a',
attr: {
text: { selector: ':scope', attr: 'text' },
href: { selector: ':scope', attr: 'href', type: 'url' }
}
}
})links returns URLs only. A nested extract rule turns each anchor into { text, href }; the :scope selector points both child rules at the anchor itself. That is what a link audit or a sitemap check usually needs.
- links Every anchor as an absolute, deduplicated URL. mailto, javascript and empty hrefs are dropped.
- emails Addresses from mailto links and plain text, as bare strings.
- selectorAll Overrides the default a selector of links to scope the sweep.
- type url resolves and validates links, email finds addresses. Keep them when changing the scope.
- prerender Forces a browser for contact pages and menus rendered on the client.
Both methods read one page per call; they do not crawl. To list every page a site wants indexed, the sitemap tool reads its sitemap.xml, and you can run links() or emails() on each URL it returns.
Why dedicated link and email methods beat a regex over HTML
Links and emails look trivial until you store them. The methods do the resolving, validating and deduplicating that every hand-rolled extractor ends up reimplementing.
Under the hood links is a rule of selectorAll a, attr href and type url, so you can override any part of it or write the rule yourself in a website to JSON schema.
Contact pages rendered by JavaScript need prerender: true and a waitForSelector; scraping JavaScript-rendered pages explains the waits.
When not to: obfuscated addresses such as name [at] domain [dot] com, emails in images or links assembled by JavaScript are not detected. Parse those yourself with npm packages in a remote function.
FAQ
How do I extract all links from a website page with an API?
Does the email extractor API find obfuscated addresses?
Can I get the anchor text along with each extracted URL?
Does extracting links crawl the whole website?
Can I get links and emails together with the page content?
Solve the next problem with the same API
LLM context from any URL
Any website to JSON
Tables and lists to JSON arrays
Custom fields from JavaScript apps
JavaScript with npm packages
Only the fields you need
Ready to extract every link and email?
Absolute URLs and bare email addresses from any page, one method each. Start on the free tier and try it on your own site.