{"slug": "alternative-data-when-a-block-looks-like-a-data-point", "title": "Alternative Data: When a Block Looks Like a Data Point", "summary": "Exabel's January 2026 survey of 100 fundamental portfolio managers and analysts managing $610 billion found that 71% cite combining data from different sources as the most frustrating part of working with alternative data, while 94% already use AI or machine learning in research. The blog argues that web-collected data panels often suffer from silent collection failures—such as bot defenses returning empty 200 responses, changing exit-country vantage points, and rotation stopping at the first response—which corrupt time series and mislead models. It recommends defining validation rules at the request level, as implemented in Foura's API, to ensure data integrity.", "body_md": "A retailer's product page answered with status 200 and nothing in it. The pipeline didn't log a block. It logged an empty shelf.\n\nThat's the failure nobody budgets for in alternative data. Not a missing dataset, not a slow vendor. A collection layer that keeps handing you rows while quietly measuring something other than the company you're covering.\n\n## The Challenge\n\nIn January 2026, Exabel surveyed 100 fundamental portfolio managers and analysts across the US, UK, Singapore and Hong Kong, managing roughly $610 billion between them. [71% named combining data from different sources as the most frustrating part of working with alternative data](https://www.exabel.com/blog/2026-alternative-data-market-report-out-now/), and 94% said they already run AI or machine learning somewhere in their research process.\n\nRead those two numbers next to each other and the shape of the problem shows up. The modeling side is well staffed. The plumbing underneath it isn't.\n\nA web-collected panel (prices, stock status, careers pages, review counts, marketplace assortment) is a time series before it's anything else. Every time series carries an assumption that nobody writes into the spec: today's observation was collected the same way yesterday's was. Break that assumption loudly and you get an alert. Break it quietly and you get a signal.\n\nThree of those quiet breaks show up in almost every web-collected panel.\n\n**The 200 that isn't content.** Bot defenses stopped answering with a clean 403 a long time ago. A challenge page, a consent wall, or an empty-results template arrives with a success status and a body your parser reads as zero results. Fewer listings looks identical to less demand.\n\n**The vantage point moved.** Price, currency, assortment, promotional banner, sometimes whether the page renders at all: retailers decide all of it from where the request appears to come from. If Monday's collection exited in Germany and Thursday's exited in Poland, there's a step in your series that belongs to you, not to the retailer.\n\n**The rotation that took the first answer it got.** Rotate through exits without telling the collector what success means, and it stops at the first exit that responds. A refusal is a response. So the row lands, the job goes green, and nobody looks again.\n\nNone of these throw an exception. Ingestion counts rows, the dashboard stays green, and the analyst gets a chart. Then the model spends a quarter learning the behavior of your collection infrastructure instead of the behavior of the business.\n\n## The Approach\n\nTreat data integrity as a property of the request, not of the parser downstream. Three things have to be true.\n\n**1. Say what a real page looks like.** The collector has no way to work it out on its own. Give it a string that only genuine content carries and a couple that only refusals carry, and a challenge page stops counting as an observation. We wrote about this when [validate rules landed](/blog/validate-decides-success): the request itself decides what success means.\n\n``` python\nimport requests\n\nr = requests.post(\n    \"https://api.foura.ai/api/proxy\",\n    headers={\"X-API-Key\": \"YOUR_API_KEY\"},\n    json={\n        \"maxTries\": 8,\n        \"exitCountries\": [\"DE\"],\n        \"request\": {\n            \"method\": \"GET\",\n            \"url\": \"https://retailer.example/p/12345\",\n            \"validate\": {\n                \"status\": {\"accept\": [200]},\n                \"data\": {\n                    \"accept\": [\"data-testid=\\\"price\\\"\"],\n                    \"fail\": [\"Access Denied\", \"Just a moment\"]\n                }\n            }\n        }\n    }\n).json()\n\nobservation = r[\"data\"]        # content your rules accepted, or nothing\nexit_id = r[\"proxy\"]           # opaque ID of the exit that delivered it\nserved_from = r[\"exitCountry\"] # verify it against what you asked for\n```\n\nRotation now has a definition of done. It keeps trying exits until one returns something that passes your rules, instead of returning the first page-shaped object it meets.\n\n**2. Hold the vantage point still.** `exitCountries`\n\nis a strict allowlist of target-visible country codes, and exits with unknown geography are left out rather than substituted. Two honest caveats, both worth knowing before you build on it. Country metadata is refreshed on a cycle (normally within about ten minutes), so it isn't a live lookup at request time, which is exactly why the response carries `exitCountry`\n\nfor you to check. And when the pool has no match for the scope you asked for, the call comes back with HTTP 200 and an error envelope rather than an exception. Read the body, not the status. Preserve the scope and retry later instead of widening it, because a widened scope is a break in the series.\n\n**3. Keep the identity of the exit.** The `proxy`\n\nfield is an opaque ID, not an address. Pass it back on a follow-up Single or Browser call and the detail page comes from the same vantage point as the search page that found it ([how to reuse an exit](/docs/how-to/reuse-a-proxy)). Store that ID and the `X-FourA-Request-Id`\n\nheader alongside every row. When an analyst questions a spike six weeks later, the question \"was this real?\" becomes a lookup instead of an argument.\n\nFor a source you're onboarding and don't understand yet, [Auto](/blog/auto-endpoint) is the fastest way to find a working path: it walks the cheap-to-expensive ladder, tells you which rung won, and hands back the session that worked. Use it to find the route, then put production volume on the direct engines. Replay that session through Single where nothing needs rendering, Browser where it genuinely does. Path finding and steady-state collection are different jobs.\n\n## Results\n\nWhat changes when those three properties hold, on a panel of a few thousand product pages a day across a dozen retailers (illustrative scenario based on industry benchmarks):\n\n**A gap is a gap, not a guess.** Refusals never enter the panel as zeros, so an empty result set means the retailer showed nothing, and your absence signal is worth trading on.**Comparable rows.** Every observation in a series comes from the country the series is defined in, so a price move is a price move.**Reproducible history.** Exit ID plus request ID per row means any disputed data point can be traced back to the exact call that produced it.**Cheaper per usable row.** Requests that would have produced a discarded observation get retried at collection time rather than paid for, parsed, stored, and later cleaned out of a backtest.\n\nBut the last one is what research teams underrate. A bad row isn't free just because it was cheap to fetch. It costs a research cycle, and sometimes it costs the confidence of the person who has to sign off on the signal.\n\n## Key Takeaway\n\nAlt data buyers evaluate providers on coverage, latency, and history depth. Almost nobody asks the question that decides whether a panel is tradeable: how does this dataset behave on the day the source refuses to answer?\n\nA vendor that drops the row is honest. A vendor that returns a courtesy page as an observation has sold you a measurement of their own infrastructure, and you'll pay for it in a backtest that works right up until it doesn't. That question belongs in every data diligence checklist, and it belongs in your own pipeline first, because if you're collecting it yourself, you're the vendor.", "url": "https://wpnews.pro/news/alternative-data-when-a-block-looks-like-a-data-point", "canonical_source": "https://foura.ai/blog/alternative-data-silent-collection-gaps", "published_at": "2026-08-19 08:59:42+00:00", "updated_at": "2026-08-19 09:12:33.017645+00:00", "lang": "en", "topics": ["artificial-intelligence", "machine-learning"], "entities": ["Exabel", "Foura"], "alternates": {"html": "https://wpnews.pro/news/alternative-data-when-a-block-looks-like-a-data-point", "markdown": "https://wpnews.pro/news/alternative-data-when-a-block-looks-like-a-data-point.md", "text": "https://wpnews.pro/news/alternative-data-when-a-block-looks-like-a-data-point.txt", "jsonld": "https://wpnews.pro/news/alternative-data-when-a-block-looks-like-a-data-point.jsonld"}}