---
name: fetch_iab_gvl
kind: function
lang: py
domain: cybersecurity
version: "1.0.0"
purity: impure
signature: "def fetch_iab_gvl(out_path: str = \"\", url: str = \"\", lang: str = \"\") -> dict"
description: "Descarga y parsea la Global Vendor List (GVL) de IAB Europe TCF: el catalogo maestro de data brokers (vendors) con sus propositos de tratamiento, intereses legitimos, special purposes, features y categorias de datos. Recon de privacidad/tracking."
tags: [consent, tcf, gvl, iab, privacy, data-brokers, vendor-list, recon, cmp]
params:
  - name: out_path
    desc: "Ruta de archivo donde guardar el JSON crudo descargado. Si vacio no guarda nada. Crea los directorios padre si no existen."
  - name: url
    desc: "Endpoint de la GVL. Si vacio usa el endpoint TCF v3.2 por defecto (vendor-list.consensu.org/v3/vendor-list.json) y, si falla, hace fallback al v2."
  - name: lang
    desc: "Codigo de idioma ISO opcional (ej. es). NO cambia el endpoint principal: las traducciones de propositos viven en endpoints aparte (purposes-<lang>.json). Hoy solo se acepta el parametro; no se descargan traducciones."
output: "dict resumen de la GVL. En exito status=ok con versiones (gvlSpecificationVersion, vendorListVersion, tcfPolicyVersion), lastUpdated, contadores (n_vendors, n_purposes, n_specialPurposes, n_features, n_dataCategories) y los mapas vendors / purposes / dataCategories indexados por id (string). En fallo de red o parseo status=error con el mensaje; nunca lanza excepcion."
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_py_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/cybersecurity/fetch_iab_gvl.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from cybersecurity.fetch_iab_gvl import fetch_iab_gvl

# Descarga real del endpoint v3 (fallback automatico a v2 si falla) y guarda
# el JSON crudo para inspeccion posterior.
gvl = fetch_iab_gvl(out_path="/tmp/gvl.json")
print(gvl["status"])                 # ok
print(gvl["vendorListVersion"])      # ej. 163
print(gvl["n_vendors"])              # > 1000
# Mirar un vendor concreto (Google = id 755 en TCF v3)
print(gvl["vendors"].get("755", {}).get("name"))
```

Lanzable directo desde la raiz del registry:

```bash
python/.venv/bin/python3 python/functions/cybersecurity/fetch_iab_gvl.py /tmp/gvl.json
```

## Cuando usarla

Usala cuando hagas recon de privacidad/tracking de un sitio web y necesites
mapear los `vendorId` que aparecen en una cookie de consentimiento (TC String /
__tcfapi) a nombres reales de empresas, sus propositos de tratamiento y sus
politicas de privacidad. Es el primer paso para auditar quien recibe los datos
del usuario via un CMP que implementa el IAB Europe TCF. Tambien para construir
un dataset local de data brokers (los `vendors`) y sus declaraciones de datos.

## Gotchas

- **Impura, hace HTTP.** Depende de que `vendor-list.consensu.org` este accesible.
  En fallo de red o JSON corrupto devuelve `{"status": "error", "error": "..."}`
  y NO lanza — el caller DEBE comprobar `status` antes de usar el resultado.
- **Fallback v3 -> v2.** Si no pasas `url`, intenta v3 y luego v2. Si pasas `url`
  explicito, solo se intenta esa (sin fallback).
- **`policyUrl` derivado.** En GVL v3 los vendors NO tienen un campo `policyUrl`
  directo; la URL de privacidad vive en `urls[].privacy` (lista por idioma).
  La funcion la deriva tolerando ambos formatos (v2/v3) y devuelve `""` si no hay.
- **`dataCategories` puede faltar** en versiones antiguas (v2). Se tolera la
  ausencia: `n_dataCategories` sera 0 y el mapa estara vacio.
- **`lang` no descarga traducciones.** El parametro existe para la firma futura,
  pero hoy el resumen siempre viene del endpoint principal (textos en ingles).
  Las traducciones de propositos estan en endpoints separados
  (`.../purposes-es.json`) que esta funcion no consulta todavia.
- **Payload grande** (~varios MB, >1000 vendors). El dict resumido recorta cada
  vendor a los campos utiles, pero sigue siendo grande: no lo imprimas entero.
