# Spain Tax Rulings Scraper (DGT Consultas Vinculantes) (`gefese/spain-tax-rulings-scraper`) Actor

Searches the public database of Spanish tax rulings (Consultas Tributarias of the Direccion General de Tributos, binding and general, 1997 onwards) and returns ruling number, date, issuing body, legislation, facts, question and full answer. Unofficial tool, not affiliated with Hacienda or the DGT.

- **URL**: https://apify.com/gefese/spain-tax-rulings-scraper.md
- **Developed by:** [Gonzalo Fajardo](https://apify.com/gefese) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 result items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Spain Tax Rulings Scraper (DGT Consultas Vinculantes)

Unofficial tool. It is not affiliated with, endorsed by or connected to the Ministerio de Hacienda or the Dirección General de Tributos (DGT).

### What it does

The Dirección General de Tributos publishes its written replies to taxpayers' questions ("Consultas Tributarias", 1997 onwards) in a public search application. This Actor searches that database with your search terms and returns one dataset item per ruling, including the full text of the answer. Binding rulings (*consultas vinculantes*, numbers such as `V5460-26`) and general rulings (*consultas generales*, numbers such as `0018-23`) are both supported. Useful for tax advisers, legal-tech teams, researchers and anyone building a Spanish tax knowledge base (Spanish keywords: consultas tributarias, doctrina administrativa, IRPF, IVA, Impuesto sobre Sociedades, criptomonedas, vivienda habitual).

Fields returned per ruling:

- `rulingNumber`: number as published (for example `V5460-26`)
- `rulingType`: `binding` or `general`
- `date`: date of issue ("Fecha salida") as `yyyy-mm-dd`
- `issuingBody`: the DGT unit that issued the reply ("Órgano")
- `legislation`: legislation cited ("Normativa"), one reference per line
- `facts`: description of the facts ("Descripción de hechos")
- `question`: question put to the administration ("Cuestión planteada")
- `answer`: full answer ("Contestación completa"), paragraphs separated by line breaks
- `url`: link that opens the ruling in the official search application
- `scrapedAt`: UTC time of scraping

Fields the site does not provide are left `null`, never guessed. General rulings have no separate facts field (the facts are part of the answer text), so `facts` is `null` for them, and some rulings cite no legislation. E-mail addresses, should the text contain any, are replaced by `[email removed]`. The site does not publish a ruling's subject tags, so none are returned.

### How to run it

1. Open the Actor and keep the prefilled input: search term `vivienda habitual`, ruling type `binding`, `maxItems` 10.
2. Click Start. The run opens the search application, runs the search and reads each ruling one at a time.
3. When the run has finished, open the Output tab for the table or download the dataset as JSON, CSV or Excel.
4. To search for something else, replace the search term. Add several terms to run several searches in one run; a ruling found twice is returned once.
5. To narrow by period, fill `dateFrom` and/or `dateTo` (`yyyy-mm-dd`). With only `dateFrom` the end is today; with only `dateTo` the start is 1997-01-01.

### How much does it cost to scrape the DGT tax rulings database?

The Actor uses pay-per-event pricing: 0.002 USD per ruling (one `result` event per dataset item). 1,000 rulings cost 2 USD, 100 rulings cost 0.20 USD. The site is slow and is read politely (one request at a time, at least one second apart, one extra request per ruling), so expect roughly 2 to 9 seconds per ruling: the prefill of 10 rulings took 17 to 94 seconds in local runs, and 30 rulings took 265 seconds in one local run.

### Input example

```json
{
  "queries": ["criptomonedas"],
  "rulingTypes": ["binding", "general"],
  "dateFrom": "2023-01-01",
  "dateTo": "2025-12-31",
  "maxItems": 30
}
```

- `queries`: search terms sent to the site's free-text field (Spanish works best). A search with no hits ends successfully with an empty dataset.
- `rulingTypes`: `binding`, `general` or both (default both; binding rulings come first).
- `dateFrom`, `dateTo`: optional date range on the issue date.
- `maxItems`: total cap across all terms and types (default 50, prefill 10).

### Output example

Three items from a local run with the prefill (long texts shortened here with "..."; the dataset holds the full text):

```json
[
  {
    "rulingNumber": "V5487-26",
    "rulingType": "binding",
    "date": "2026-08-18",
    "issuingBody": "SG de Impuestos sobre las Personas Jurídicas",
    "legislation": "Ley 27/2014 Impuesto sobre Sociedades - 10 - 11\nLey 37/1992 Impuesto sobre el Valor Añadido IVA - 5 - 7 - 8 - 11 - 20 - 78",
    "facts": "La consultante es una entidad mercantil que es propietaria de una finca rústica en la que existen unas edificaciones destinadas a la explotación ganadera (granj...",
    "question": "Tratamiento fiscal de las cantidades pagadas en concepto de alquiler y la cantidad entregada en concepto de a cuenta de la compra para el arrendador a efectos d...",
    "answer": "IMPUESTO SOBRE SOCIEDADES\nEl artículo 10.3 de la Ley 27/2014, de 27 de noviembre, del Impuesto sobre Sociedades (en adelante, LIS) establece que:\n“3. En el méto...",
    "url": "https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V5487-26",
    "scrapedAt": "2026-10-05T12:40:08Z"
  },
  {
    "rulingNumber": "V5462-26",
    "rulingType": "binding",
    "date": "2026-08-11",
    "issuingBody": "SG de Impuestos sobre el Consumo",
    "legislation": "Ley 37/1992 Impuesto sobre el Valor Añadido IVA - 20.1.20",
    "facts": "La consultante es una entidad mercantil que se dedica a la actividad inmobiliaria que adquirió un solar y lo segregó en tres solares (de 150 metros cuadrados ca...",
    "question": "Tipo del Impuesto sobre el Valor Añadido aplicable a la transmisión de la referida vivienda.",
    "answer": "1.- El artículo 4, apartado uno de la Ley 37/1992, de 28 de diciembre, del Impuesto sobre el Valor Añadido (BOE de 29 de diciembre), establece que \"estarán suje...",
    "url": "https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V5462-26",
    "scrapedAt": "2026-10-05T12:40:08Z"
  },
  {
    "rulingNumber": "V5460-26",
    "rulingType": "binding",
    "date": "2026-08-11",
    "issuingBody": "SG de Impuestos sobre el Consumo",
    "legislation": "Ley 37/1992 Impuesto sobre el Valor Añadido IVA - 91",
    "facts": "La consultante, persona física, va a arrendar un local a una psicóloga para dedicarlo a su actividad.",
    "question": "A efectos del Impuesto sobre el Valor Añadido, se cuestiona la tributación de la operación",
    "answer": "1.- El artículo 4, apartado uno de la Ley 37/1992, de 28 de diciembre, del Impuesto sobre el Valor Añadido (BOE de 29 de diciembre), establece que “estarán suje...",
    "url": "https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V5460-26",
    "scrapedAt": "2026-10-05T12:40:09Z"
  }
]
```

### Limitations and the free plan

- Only the public search application at petete.tributos.hacienda.gob.es is read; no login, no captcha, no proxies. Before anything else the Actor reads the site's robots.txt and obeys it. If robots.txt cannot be read (for example the host answers 5xx), the run stops with a clear status message instead of scraping.
- The site is sometimes slow or answers 502/503. Failed requests are retried three times (after 5, 15 and 30 seconds); a ruling that still fails is skipped and logged. If no search can be completed at all, the run fails.
- The site returns 20 rulings per results page; the Actor pages through the results until `maxItems`. The site can refuse a search it considers too slow (it has an HTTP 408 message for that); the run then reports it and you can narrow the date range.
- The search is the site's own: its relevance, stop-word and phrase rules apply, and results are ordered newest first. The site's "criterio de interés" flag and rulings' internal ids are not returned.
- The text is the Administration's reply as published. This Actor does not give tax advice, and a ruling binds the tax authorities only with respect to the person who asked.
- Free plan: the prefill (10 rulings) is billed as 10 result events, 0.02 USD; larger runs scale linearly in cost and in time (roughly 2 to 9 seconds per ruling). Use `maxItems` to keep a run small.

### FAQ and support

**Does it return the names of the people who asked?** No. The rulings are published anonymised ("el consultante"); the Actor reads only the fields listed above, and replaces e-mail addresses should the text contain one.

**Why is `facts` empty for some rulings?** General rulings do not have a separate "Descripción de hechos" field on the site.

**Can I get the rulings of one year?** Yes: set `dateFrom` and `dateTo`, with a search term or with `queries` left empty.

**Something is wrong or a field is missing.** Please report it in the Issues tab of this Actor on Apify, with the input you used.

# Actor input Schema

## `queries` (type: `array`):

Free-text search terms (Spanish works best), one search per entry, for example "vivienda habitual" or "criptomonedas". Each term is sent to the site's "Texto libre" field. Leave empty only if you set a date range.

## `rulingTypes` (type: `array`):

Which databases to search: binding rulings (consultas vinculantes, from 1997, with facts, question and answer) and/or general rulings (consultas generales, which have no separate facts field). Binding rulings are returned first.

## `dateFrom` (type: `string`):

Optional. Only rulings issued on or after this date (the site's "Fecha salida"). If only this is set, the end date is today.

## `dateTo` (type: `string`):

Optional. Only rulings issued on or before this date. If only this is set, the start date is 1997-01-01.

## `maxItems` (type: `integer`):

Stops after this many rulings in total across all search terms and ruling types. Each ruling is one billed result. The site returns 20 per page and each ruling needs one more request, so 50 rulings take about two minutes.

## Actor input object example

```json
{
  "queries": [
    "vivienda habitual"
  ],
  "rulingTypes": [
    "binding"
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "vivienda habitual"
    ],
    "rulingTypes": [
        "binding"
    ],
    "dateFrom": "",
    "dateTo": "",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("gefese/spain-tax-rulings-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["vivienda habitual"],
    "rulingTypes": ["binding"],
    "dateFrom": "",
    "dateTo": "",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("gefese/spain-tax-rulings-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "vivienda habitual"
  ],
  "rulingTypes": [
    "binding"
  ],
  "dateFrom": "",
  "dateTo": "",
  "maxItems": 10
}' |
apify call gefese/spain-tax-rulings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gefese/spain-tax-rulings-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/1CGwaGjme1Ys4CEeK/builds/wUGuqB44QHx9duZam/openapi.json
