Scrape a website
Give a page address and a short list pairing names like price or title with CSS selectors, and get exactly those values back, name by name: one match gives a value, several give a list, none gives an empty result. You pay per use, with no subscription.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
point at CSS selectors, get JSON
Point at the data you already know
CSS selectors have described the structure of web pages since the first version of CSS in 1996, and browsers agreed long ago on how to match them. That is why they remain the fastest and cheapest way to pull a known detail from a known layout. You give the tool a page address and a short list that pairs each name you choose, such as price or title, with the selector that points at it. The page is opened, including content its scripts add after loading, and the result mirrors your list name by name.
Predictable results you can test
Unlike tools that interpret a page, a selector either matches or it does not: there is no ambiguity and no invented value. A selector that finds one element gives its value, one that finds several gives a list, such as the rows of a table, and one that finds nothing gives an empty result you can spot at once. That makes it a good fit for recurring jobs on stable layouts, like pulling address, price, floor area and photo count from thousands of property listings with one list of selectors, or checking a careers page each morning for new openings before any public feed exists.
Why an idea from 1996 still works best
Selectors have described document structure since CSS1 in 1996, and browsers standardized querySelector matching decades ago — which is exactly why they remain the fastest, cheapest way to pull a known field from a known layout. Unlike vision or language models, a selector either matches or it doesn't: zero ambiguity, zero hallucinated fields, and a result you can unit-test.
What you can do with it
Real-estate listing aggregation
Pull address, price, square footage and photo count from a listings site using a single selector map reused across thousands of pages.
Job board watching
Extract title, company and posted-date selectors from a careers page each morning to detect new openings before a public feed exists.
FAQ
Does it handle JavaScript-rendered pages?
Yes, the fetch layer renders pages before applying your CSS selectors, so content injected by client-side scripts is matchable just like static HTML.
What happens if my selector doesn't match anything?
That field comes back as null in the response instead of failing the whole task, so one broken selector in a map doesn't block the rest of your data.
What happens if a scrape fails?
Failed attempts are retried automatically up to three times; if it still fails you get a clear error and are never charged for that task.
When are CSS selectors the right choice?
When you know the page layout and it stays fairly stable. Selectors are the fastest, cheapest way to pull a known detail from a known layout, and they either match or not, so no value is ever invented.
How do I get the CSS selector of an element?
Open your browser's developer tools, right-click the element you want and copy its selector. Pair it with a name of your choice, such as price, and the result will carry that name next to the value found.
How long are the results kept?
The finished result waits behind a secure link that stays valid for 24 hours, and after that the data is deleted. It is never used to train any model.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/web/scrape-selector \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","selectors":{"titulo":"h1","parrafos":"p"}}'const res = await fetch("https://api.kit.forhosting.com/web/scrape-selector", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"url": "https://example.com",
"selectors": {
"titulo": "h1",
"parrafos": "p"
}
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/scrape-selector",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"url": "https://example.com",
"selectors": {
"titulo": "h1",
"parrafos": "p"
}
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/scrape-selector", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"url":"https://example.com","selectors":{"titulo":"h1","parrafos":"p"}}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"url":"https://example.com","selectors":{"titulo":"h1","parrafos":"p"}}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/scrape-selector", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"url": "https://example.com",
"selectors": {
"titulo": "h1",
"parrafos": "p"
}
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.scrape_selector",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |
Technical notes
Give it a URL and a map of CSS selectors, and it hands back exactly the fields you asked for as structured JSON — no headless browser to babysit, no HTML soup to parse on your end. This is the workhorse endpoint for teams who already know precisely which div, span or table cell holds the data they need.
Built for people who already know their selectors
If you can open dev tools, right-click an element and copy its selector, this endpoint is for you. It suits price monitors, inventory trackers, real-estate listing pullers, job-board watchers and any recurring job where the target page's layout is known and reasonably stable. You define a selector map once — field name to CSS selector — and every request against that page shape reuses it.
What happens after you submit a request
The call to POST /web/scrape-selector returns a task_id immediately; the actual fetch and extraction run asynchronously against our global edge, including JavaScript-rendered pages when the selector targets content injected client-side. When the task finishes you get the result either pushed to your signed webhook or waiting behind a signed link that stays valid for 24 hours, after which the data is deleted and never touches any training pipeline.
What you get back, and what happens when a selector misses
The response mirrors your selector map, field by field, with arrays for repeated elements like list items or table rows and null for a selector that matched nothing on that particular page.
Where it sits in a bigger pipeline
Because pricing is a flat per-request base plus a small per-URL fee, it scales predictably whether you are watching five competitor prices or five thousand SKUs. Most teams chain this endpoint behind a scheduler: cron-trigger a batch of URLs, let each task land on a webhook, and feed the JSON straight into a database, spreadsheet or alerting rule without a human ever opening the source page.
Integration use cases
Competitor price monitoring
Track the .price and .stock-status selectors on a set of competitor product pages daily and feed the JSON into a pricing dashboard.
Table-based data collection
Target a table row selector to turn an HTML table of exchange rates or specs into a JSON array your app can index directly.
Technical questions
Is the web scraping API free to try?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
How much does web.scrape_selector cost?
$0.040 per request plus $0.001 per URL processed, billed only when the task actually completes.
Can I scrape multiple URLs with one selector map?
Yes, submit a list of URLs alongside a single selector map when the pages share the same layout, which is the most cost-efficient way to use this endpoint.
How do I get the results back?
Either as a push to your signed webhook when the task finishes, or via a signed link valid for 24 hours if you prefer to poll.
How is this different from the AI or schema scraping endpoints?
Selector scraping is the fastest and cheapest option when you already know the exact CSS path to each field; the AI and schema endpoints exist for when layouts vary or you need guaranteed data types.