Search FINN Torget for second-hand items.
Searches FINN Torget, Norway's largest marketplace, like its search page: title, price in NOK, place, when it was published (UTC), private or business seller, shipping and FINN 'buy now' flags, map position, image and link. Filters: price range, condition, seller type, for sale / giveaway / wanted; sort by relevance, date or price. 53 listings per page.
- Site
- FINN, Norway
- Version
- v1
- Runs, last 7 days
- No reports yet
- sha256
- 987a787d6b15…
Run it with your agent
after installing WieldAsk in plain words. Your agent finds this recipe and calls it:
“Search FINN Torget for second-hand items.”
Read-only: it changes nothing on the site. No login needed.
{ "tool": "run", "arguments": { "recipe_id": "finn.no/search-marketplace", "params": { "max_price": 2000, "query": "sykkel", "sort": "newest" } } }
Requests
sent from your browser, in order- 1GET/recommerce/forsale/search/api/search/SEARCH_ID_BAP_COMMON?condition&dealer_segment&page&price_from&price_to&q&sort&trade_typeFINN's own search API (/recommerce/forsale/search/api, the apiUrl in the search page's props; the page calls it when the search changes). The one-item foreach maps the params to its filter codes.
Parameters
from your agentReturns
to your agentWhere it can send
- www.finn.no
Anything else is blocked by the extension.
Reads from your session
Nothing. Your browser attaches the site's cookies by itself.
Used only in requests to the addresses on the left. Never sent to wield.run or your agent.
Checks
a run passes when- length(output.listings) > `40`
- output.total > `1000`
- length(output.listings[?price > `2000`]) == `0`
- length(output.listings[?starts_with(url, 'https://www.finn.no/recommerce/forsale/item/')]) == length(output.listings)
- output.listings[*].published_at == reverse(sort(output.listings[*].published_at))
- length(output.listings[?currency == 'NOK']) == length(output.listings)
Raw recipe
Served as canonical JSON from /api/v1/recipes/finn.no/search-marketplace, so its sha256 matches the ETag and the index.
curl -s https://wield.run/api/v1/recipes/finn.no/search-marketplace | shasum -a 256 # 987a787d6b156a1fb9c1d9475bff8050821991d15fa72abdeb107f6e93c043ed
{ "description": "Searches FINN Torget, Norway's largest marketplace, like its search page: title, price in NOK, place, when it was published (UTC), private or business seller, shipping and FINN 'buy now' flags, map position, image and link. Filters: price range, condition, seller type, for sale / giveaway / wanted; sort by relevance, date or price. 53 listings per page.", "effect": "read", "id": "finn.no/search-marketplace", "keywords": [ "finn", "finn torget", "brukt", "til salgs", "gis bort", "sykkel", "møbler", "kjøpe", "selge", "second hand", "used", "marketplace", "norway", "classifieds" ], "output": { "schema": { "properties": { "last_page": { "type": [ "integer", "null" ] }, "listings": { "items": { "properties": { "buy_now": { "description": "Can be bought directly on FINN", "type": "boolean" }, "currency": { "type": [ "string", "null" ] }, "image": { "type": [ "string", "null" ] }, "latitude": { "type": [ "number", "null" ] }, "location": { "type": [ "string", "null" ] }, "longitude": { "type": [ "number", "null" ] }, "price": { "description": "NOK", "type": [ "number", "null" ] }, "published_at": { "description": "ISO 8601, UTC", "type": [ "string", "null" ] }, "seller": { "description": "private or business", "type": "string" }, "shipping": { "description": "Can be shipped", "type": "boolean" }, "title": { "type": "string" }, "url": { "type": "string" } }, "type": "object" }, "type": "array" }, "page": { "type": "integer" }, "query": { "type": "string" }, "total": { "type": [ "integer", "null" ] } }, "required": [ "query", "listings" ], "type": "object" }, "value": "let $s = steps.search[0] in {query: params.query, total: $s.total || `0`, page: params.page || `1`, last_page: $s.last_page || `null`, listings: map(&(let $x = @ in {title: $x.title, price: $x.price || `null`, currency: $x.currency || `null`, location: $x.location || `null`, published_at: ($x.ts && let $t = $x.ts // `1000` in let $z = $t // `86400` + `719468` in let $era = $z // `146097` in let $doe = $z - $era * `146097` in let $yoe = ($doe - $doe // `1460` + $doe // `36524` - $doe // `146096`) // `365` in let $doy = $doe - (`365` * $yoe + $yoe // `4` - $yoe // `100`) in let $mp = (`5` * $doy + `2`) // `153` in let $mon = ($mp < `10` && $mp + `3`) || $mp - `9`, $day = $doy - (`153` * $mp + `2`) // `5` + `1`, $sec = $t % `86400` in join('', [to_string($yoe + $era * `400` + (($mon <= `2` && `1`) || `0`)), '-', pad_left(to_string($mon), `2`, '0'), '-', pad_left(to_string($day), `2`, '0'), 'T', pad_left(to_string($sec // `3600`), `2`, '0'), ':', pad_left(to_string(($sec % `3600`) // `60`), `2`, '0'), ':', pad_left(to_string($sec % `60`), `2`, '0'), 'Z'])) || `null`, seller: (contains($x.flags || `[]`, 'retailer') && 'business') || 'private', shipping: contains($x.flags || `[]`, 'shipping_exists'), buy_now: contains($x.flags || `[]`, 'buy_now'), latitude: $x.lat || `null`, longitude: $x.lon || `null`, image: $x.image || `null`, url: $x.url}), $s.docs || `[]`)}" }, "params": { "additionalProperties": false, "properties": { "condition": { "description": "new (unopened), like_new, good, used (visibly used), needs_repair", "enum": [ "new", "like_new", "good", "used", "needs_repair" ], "type": "string" }, "max_price": { "description": "NOK", "minimum": 0, "type": "number" }, "min_price": { "description": "NOK", "minimum": 0, "type": "number" }, "page": { "default": 1, "maximum": 50, "minimum": 1, "type": "integer" }, "query": { "description": "What to look for, e.g. 'sykkel', 'sofa', 'iphone 13'", "maxLength": 100, "minLength": 2, "type": "string" }, "seller": { "default": "any", "enum": [ "any", "private", "business" ], "type": "string" }, "sort": { "default": "relevance", "enum": [ "relevance", "newest", "oldest", "price_low", "price_high" ], "type": "string" }, "type": { "default": "for_sale", "description": "for_sale, giveaway (free) or wanted (people looking to buy)", "enum": [ "for_sale", "giveaway", "wanted" ], "type": "string" } }, "required": [ "query" ], "type": "object" }, "provenance": { "method": "browser-inspect", "notes": "The search page https://www.finn.no/recommerce/forsale/search?q=… is server-rendered from a base64 react-query state; its props name apiUrl https://www.finn.no/recommerce/forsale/search/api, and the page calls /search/SEARCH_ID_BAP_COMMON with the same URL parameters as the page (q, price_from, price_to, condition 1–5, dealer_segment 1 private / 3 business, trade_type 1 sale / 2 giveaway / 3 wanted, sort RELEVANCE / PUBLISHED_DESC / PUBLISHED_ASC / PRICE_ASC / PRICE_DESC, page). Answer: docs [{type 'bap', heading, location, price.amount/currency_code, timestamp (ms), flags private/retailer/shipping_exists/buy_now, coordinates, image.url, canonical_url}], metadata.result_size.match_count, metadata.paging.last. Display ads are a separate advertising slot, not docs; the recipe keeps type 'bap' only. published_at is the epoch converted in JMESPath (civil-from-days). Images are links to images.finncdn.no (never called).", "recorded_at": "2026-11-07T21:53:00+01:00", "recorded_by": "C4" }, "requires_login": false, "schema": "https://wield.run/schema/recipe/v1.json", "site": { "country": "NO", "id": "finn.no", "language": "nb", "name": "FINN", "origin": "https://www.finn.no", "origins": [ "https://www.finn.no" ] }, "steps": [ { "description": "FINN's own search API (/recommerce/forsale/search/api, the apiUrl in the search page's props; the page calls it when the search changes). The one-item foreach maps the params to its filter codes.", "expect": { "assert": "body.docs != `null`", "content_type": "json", "status": [ 200 ] }, "extract": { "json": { "docs": "docs[?type == 'bap'].{title: heading, price: price.amount, currency: price.currency_code, location: location, ts: timestamp, flags: flags, lat: coordinates.lat, lon: coordinates.lon, image: image.url, url: canonical_url}", "last_page": "metadata.paging.last", "total": "metadata.result_size.match_count" } }, "foreach": { "as": "f", "in": "let $sort = params.sort || 'relevance', $cond = params.condition || `null`, $seller = params.seller || 'any', $type = params.type || 'for_sale' in [{sort: ($sort != 'relevance' && (`[[\"relevance\",\"RELEVANCE\"],[\"newest\",\"PUBLISHED_DESC\"],[\"oldest\",\"PUBLISHED_ASC\"],[\"price_low\",\"PRICE_ASC\"],[\"price_high\",\"PRICE_DESC\"]]`[?@[0] == $sort] | [0][1])) || `null`, condition: (`[[\"new\",\"1\"],[\"like_new\",\"2\"],[\"good\",\"3\"],[\"used\",\"4\"],[\"needs_repair\",\"5\"]]`[?@[0] == $cond] | [0][1]) || `null`, dealer: (`[[\"private\",\"1\"],[\"business\",\"3\"]]`[?@[0] == $seller] | [0][1]) || `null`, trade: (`[[\"for_sale\",\"1\"],[\"giveaway\",\"2\"],[\"wanted\",\"3\"]]`[?@[0] == $type] | [0][1]) || '1'}]", "max": 1 }, "id": "search", "request": { "headers": { "Accept": "application/json" }, "method": "GET", "query": { "condition": "{{f.condition}}", "dealer_segment": "{{f.dealer}}", "page": "{{params.page}}", "price_from": "{{params.min_price}}", "price_to": "{{params.max_price}}", "q": "{{params.query}}", "sort": "{{f.sort}}", "trade_type": "{{f.trade}}" }, "url": "/recommerce/forsale/search/api/search/SEARCH_ID_BAP_COMMON" } } ], "task": "Search FINN Torget for second-hand items", "tos": { "notes": "One request per call, the same search the page makes. robots.txt allows the search and its API but disallows URLs with sort= for bots; sort= is sent only when the user asks for another order than relevance. No messages, offers or favourites.", "reviewed": false }, "verify": { "checks": [ "length(output.listings) > `40`", "output.total > `1000`", "length(output.listings[?price > `2000`]) == `0`", "length(output.listings[?starts_with(url, 'https://www.finn.no/recommerce/forsale/item/')]) == length(output.listings)", "output.listings[*].published_at == reverse(sort(output.listings[*].published_at))", "length(output.listings[?currency == 'NOK']) == length(output.listings)" ], "example_params": { "max_price": 2000, "query": "sykkel", "sort": "newest" }, "max_ms": 3000 }, "version": 1 }