> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# API de riesgo de IP: geolocalización, VPN y datacenter

> Didit asigna una puntuación de riesgo a una IP con geolocalización, ISP, VPN/Tor, centro de datos, amenazas y discrepancia con el país declarado.

Usa `POST /v3/ip/risk/` cuando tu backend ya tiene una dirección IP y necesita una decisión de riesgo de servidor a servidor.

La respuesta incluye geolocalización, propiedad de la red, indicadores de VPN/Tor y de centro de datos, datos de amenazas y `country_mismatch` cuando envías un país declarado.

## Interpretar la respuesta

* `status` es `In Review` cuando el país resuelto no coincide con `claimed_country`, o cuando la dirección está asociada a infraestructura de VPN, Tor o centro de datos. En caso contrario, es `Approved`.
* `ip.ip_country_code` contiene el código del país resuelto. Compara los países mediante `ip.country_mismatch` en lugar de comparar tú mismo las cadenas de país sin procesar.
* `ip.latitude` y `ip.longitude` son una geolocalización aproximada de la red, no una ubicación precisa del dispositivo.
* `ip.is_vpn_or_tor`, `ip.is_data_center`, `ip.proxy_type` e `ip.threat` contienen evidencias de riesgo de red.
* `ip.asn_number`, `ip.asn_organization`, `ip.connection_type` e `ip.carrier` describen la red a la que pertenece la dirección.
* `request_id` es el ID de sesión de la API. Guárdalo junto con `vendor_data` si necesitas correlacionar el resultado con webhooks posteriores o con la actividad en la consola.

> [!NOTE]
>
> El enriquecimiento de IP se realiza en la medida de lo posible. Si ninguna fuente de enriquecimiento devuelve datos utilizables, el endpoint devuelve una respuesta aprobada con `ip.ip_address: null`, `ip.country_mismatch: null` y un objeto `ip.threat` vacío. Los campos de enriquecimiento individuales también pueden ser `null` cuando no están disponibles.

## Inteligencia de dispositivo

La API de riesgo de IP es un endpoint exclusivamente de servidor a servidor. La huella digital del dispositivo no lo es, porque un backend no puede observar por sí solo las señales del navegador o del dispositivo. En las sesiones del flujo alojado y del SDK, Didit expone en la respuesta de la sesión los campos de dispositivo ya recopilados, incluidos `device_fingerprint`, los detalles del dispositivo y del navegador, `user_agent`, los datos sin procesar del dispositivo y las coincidencias entre sesiones.

## Comportamiento en sandbox

Las claves de sandbox validan la solicitud y devuelven datos deterministas aprobados. No contactan con servicios de inteligencia externos, no consumen créditos, no crean una sesión ni contribuyen a Networks.

## Facturación

Las llamadas a la API de riesgo de IP cuestan \$0.02 a precio de lista. Una solicitud en producción se factura en cuanto se inicia la consulta.


## OpenAPI

````yaml es/openapi-25.json POST /v3/ip/risk/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: API de verificación de identidad. Autentícate con el encabezado x-api-key.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/ip/risk/:
    post:
      tags:
        - Standalone APIs
      summary: Calcular la puntuación de riesgo de una dirección IP
      description: >-
        Devuelve la geolocalización, la propiedad de la red, los indicadores de
        VPN/Tor y de centro de datos, datos de amenazas y `country_mismatch`
        para una dirección IP proporcionada. El endpoint crea una sesión de API
        y devuelve su id como `request_id`.


        **Decisión.** `status` es `In Review` cuando el país resuelto no
        coincide con `claimed_country`, o cuando la dirección está asociada a
        infraestructura de VPN, Tor o centros de datos. En caso contrario, es
        `Approved`.


        **En la medida de lo posible.** El enriquecimiento de IP se realiza en
        la medida de lo posible. Si ninguna fuente de enriquecimiento devuelve
        datos utilizables, el endpoint devuelve una respuesta aprobada con
        `ip.ip_address: null`, `ip.country_mismatch: null` y un objeto
        `ip.threat` vacío.


        **Facturación.** Las llamadas en producción usan los precios de la IP
        Risk API. La solicitud es facturable en cuanto comienza la consulta.


        **Sandbox.** Las claves de sandbox validan la solicitud y devuelven
        datos aprobados deterministas sin consumir créditos, crear una sesión ni
        contribuir a Networks.


        **Autenticación.** Envía la clave de API de tu aplicación en
        `x-api-key`. Si las credenciales faltan o no son válidas, se devuelve
        `403`: esta API nunca devuelve `401`.


        **Límite de solicitudes.** Cupo de escritura compartido de 300
        solicitudes por minuto por clave de API entre todos los endpoints
        POST/PATCH/DELETE.
      operationId: post_v3ip_risk
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IPRiskRequest'
            examples:
              Minimal:
                summary: Puntuar una dirección con los valores predeterminados
                value:
                  ip_address: 8.8.8.8
              With claimed country:
                summary: Puntuar con un país declarado para detectar discrepancias
                value:
                  ip_address: 8.8.8.8
                  claimed_country: ESP
                  user_agent: >-
                    Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
                    AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4
                    Safari/605.1.15
                  vendor_data: lead-123
      responses:
        '200':
          description: >-
            La evaluación de riesgo se completó. Consulta `status` para ver la
            decisión; el código HTTP siempre es `200` en las consultas
            completadas.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IPRiskResponse'
              examples:
                Clean residential IP:
                  summary: Dirección de residencia; el país declarado coincide
                  value:
                    request_id: 9a96ed2a-721e-4b86-8cbf-d61eda529279
                    status: Approved
                    ip:
                      status: Approved
                      ip_address: 192.0.2.10
                      ip_country: Spain
                      ip_country_code: ES
                      ip_state: Madrid
                      ip_city: Madrid
                      latitude: 40.4168
                      longitude: -3.7038
                      isp: Example Telecom
                      organization: Example Telecom SA
                      is_vpn_or_tor: false
                      is_data_center: false
                      time_zone: Europe/Madrid
                      time_zone_offset: '+0200'
                      asn_number: 3352
                      asn_organization: EXAMPLE-TELECOM
                      connection_type: broadband
                      carrier: Example Telecom
                      proxy_type: null
                      claimed_country: ESP
                      country_mismatch: false
                      threat: {}
                    vendor_data: lead-123
                    metadata: null
                    created_at: '2026-09-01T10:30:00.123456+00:00'
                Datacenter mismatch:
                  summary: >-
                    Dirección de centro de datos que no coincide con el país
                    declarado
                  value:
                    request_id: 5b1d4e9a-3c7b-4f8e-9a2d-1b0c9d8e7f6a
                    status: In Review
                    ip:
                      status: In Review
                      ip_address: 8.8.8.8
                      ip_country: United States
                      ip_country_code: US
                      ip_state: California
                      ip_city: Mountain View
                      latitude: 37.386
                      longitude: -122.0838
                      isp: Google
                      organization: Google LLC
                      is_vpn_or_tor: true
                      is_data_center: true
                      time_zone: America/Los_Angeles
                      time_zone_offset: '-0700'
                      asn_number: 15169
                      asn_organization: GOOGLE, US
                      connection_type: hosting
                      carrier: null
                      proxy_type: VPN
                      claimed_country: ESP
                      country_mismatch: true
                      threat:
                        is_proxy: true
                        is_data_center: true
                        is_tor: false
                    vendor_data: lead-126
                    metadata: null
                    created_at: '2026-09-01T10:30:00.123456+00:00'
        '400':
          $ref: '#/components/responses/IPRiskValidationError'
        '403':
          $ref: '#/components/responses/RiskPermissionDenied'
        '429':
          $ref: '#/components/responses/RateLimitedWrite'
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: |-
            curl -X POST https://verification.didit.me/v3/ip/risk/ \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{
                "ip_address": "8.8.8.8",
                "claimed_country": "ESP",
                "user_agent": "Mozilla/5.0",
                "vendor_data": "lead-123"
              }'
        - lang: python
          label: Python
          source: |-
            from didit import Didit

            # Reads DIDIT_API_KEY.
            client = Didit()

            result = client.ip.assess_risk(
                ip_address="8.8.8.8",
            )
            print(result)
        - lang: javascript
          label: Node.js
          source: |-
            import Didit from "@didit-protocol/sdk-node";

            // Reads DIDIT_API_KEY.
            const didit = new Didit();

            const result = await didit.ip.assessRisk({
              ip_address: "8.8.8.8",
            });
            console.log(result);
        - lang: go
          label: Go
          source: "ctx := context.Background()\n// Reads DIDIT_API_KEY.\nclient := didit.NewClient()\n\nresult, err := client.IP.AssessRisk(ctx, didit.IPRiskRequest{\n\tIPAddress: \"8.8.8.8\",\n})\nif err != nil {\n\tlog.Fatal(err)\n}\nfmt.Printf(\"%+v\\n\", result)"
        - lang: java
          label: Java
          source: |-
            import me.didit.Didit;
            import me.didit.types.IPRiskRequest;

            // Reads DIDIT_API_KEY.
            Didit client = new Didit();

            var result = client.ip().assessRisk(IPRiskRequest.builder()
                    .ipAddress("8.8.8.8")
                    .build());
            System.out.println(result);
        - lang: php
          label: PHP
          source: |-
            <?php

            require 'vendor/autoload.php';

            use Didit\DiditClient;

            // Reads DIDIT_API_KEY.
            $didit = new DiditClient();

            $result = $didit->ip->assessRisk([
                'ip_address' => '8.8.8.8',
            ]);
            print_r($result->toArray());
        - lang: ruby
          label: Ruby
          source: |-
            require "didit"

            # Reads DIDIT_API_KEY.
            didit = Didit::Client.new

            result = didit.ip.assess_risk(ip_address: "8.8.8.8")
            puts result.to_h
        - lang: csharp
          label: .NET
          source: |-
            using System;
            using System.Text.Json;
            using Didit;

            // Reads DIDIT_API_KEY.
            using var client = new DiditClient();

            var result = await client.Ip.AssessRiskAsync(new IPRiskRequest
            {
                IpAddress = "8.8.8.8",
            });
            Console.WriteLine(JsonSerializer.Serialize(result));
components:
  schemas:
    IPRiskRequest:
      type: object
      required:
        - ip_address
      properties:
        ip_address:
          type: string
          format: ip
          description: Dirección IPv4 o IPv6 que se va a puntuar.
          example: 8.8.8.8
        claimed_country:
          type: string
          maxLength: 3
          description: >-
            País ISO 3166-1 alfa-2 o alfa-3 opcional declarado por el usuario o
            la sesión. Se usa para calcular `country_mismatch`.
          example: ESP
        user_agent:
          type: string
          maxLength: 512
          description: >-
            User agent observado por tu backend. Se almacena como contexto de la
            solicitud; no proporciona huella digital del dispositivo.
        vendor_data:
          type: string
          description: >-
            Tu referencia estable para esta solicitud. Se guarda en la sesión de
            API y se devuelve en la respuesta.
        metadata:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Tus metadatos JSON no sensibles. Se almacenan en la sesión de API y
            se devuelven en la respuesta.
    IPRiskResponse:
      type: object
      required:
        - request_id
        - status
        - created_at
      properties:
        request_id:
          type: string
          format: uuid
          description: >-
            ID de la sesión de API creada para esta solicitud. Guárdalo junto
            con `vendor_data` si necesitas relacionar el resultado con webhooks
            posteriores o con la actividad en la consola.
        status:
          type: string
          enum:
            - Approved
            - In Review
          description: >-
            Decisión para esta sesión de API: `In Review` ante riesgo de red o
            de discrepancia de país; en caso contrario, `Approved`.
        vendor_data:
          type: string
          nullable: true
          description: >-
            Devuelve el `vendor_data` que enviaste, o `null` si no enviaste
            ninguno.
        metadata:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Devuelve el `metadata` que enviaste, o `null` si no enviaste
            ninguno.
        created_at:
          type: string
          format: date-time
          description: Marca de tiempo de esta respuesta.
        ip:
          type: object
          description: >-
            Resultado de riesgo de la IP. Los campos de enriquecimiento
            individuales pueden ser `null` cuando la consulta no devolvió datos
            utilizables.
          required:
            - status
            - threat
          properties:
            status:
              type: string
              enum:
                - Approved
                - In Review
              description: >-
                `In Review` cuando el país resuelto no coincide con
                `claimed_country`, o cuando la dirección está asociada a una
                VPN, a Tor o a infraestructura de centros de datos. En caso
                contrario, `Approved`.
            ip_address:
              type: string
              nullable: true
              description: >-
                La dirección evaluada. `null` cuando ninguna fuente de
                enriquecimiento devolvió datos utilizables.
            ip_country:
              type: string
              nullable: true
              description: Nombre del país resuelto.
            ip_country_code:
              type: string
              nullable: true
              description: >-
                Código de país resuelto. Compara países mediante
                `country_mismatch` en lugar de comparar tú mismo las cadenas de
                país sin procesar.
            ip_state:
              type: string
              nullable: true
              description: Estado o región resueltos.
            ip_city:
              type: string
              nullable: true
              description: Ciudad resuelta.
            latitude:
              type: number
              nullable: true
              description: >-
                Geolocalización aproximada de la red, no una ubicación precisa
                del dispositivo.
            longitude:
              type: number
              nullable: true
              description: >-
                Geolocalización aproximada de la red, no una ubicación precisa
                del dispositivo.
            isp:
              type: string
              nullable: true
              description: >-
                Proveedor de servicios de Internet que da servicio a la
                dirección.
            organization:
              type: string
              nullable: true
              description: Organización propietaria del bloque de direcciones.
            is_vpn_or_tor:
              type: boolean
              nullable: true
              description: >-
                `true` cuando la dirección está asociada a infraestructura de
                VPN o Tor.
            is_data_center:
              type: boolean
              nullable: true
              description: >-
                `true` cuando la dirección pertenece a una red de hosting o de
                centro de datos.
            time_zone:
              type: string
              nullable: true
              description: Zona horaria de la ubicación resuelta.
            time_zone_offset:
              type: string
              nullable: true
              description: Desfase UTC de la ubicación resuelta.
            asn_number:
              type: number
              nullable: true
              description: Número de sistema autónomo.
            asn_organization:
              type: string
              nullable: true
              description: Organización que opera el sistema autónomo.
            connection_type:
              type: string
              nullable: true
              description: Clasificación de la conexión (p. ej., `cellular`, `hosting`).
            carrier:
              type: string
              nullable: true
              description: Operador móvil cuando la dirección pertenece a una red móvil.
            proxy_type:
              type: string
              nullable: true
              description: >-
                Clasificación de proxy cuando la dirección está detrás de un
                proxy.
            claimed_country:
              type: string
              nullable: true
              description: Devuelve el `claimed_country` que enviaste.
            country_mismatch:
              type: boolean
              nullable: true
              description: >-
                `true` cuando el país resuelto difiere de `claimed_country`.
                `null` cuando no hay datos de enriquecimiento o no hay un país
                declarado disponible.
            threat:
              type: object
              additionalProperties: true
              description: >-
                Evidencia libre de amenazas de red procedente de la fuente de
                enriquecimiento (p. ej., `is_proxy`, `is_tor`). Objeto vacío
                cuando no hay datos de enriquecimiento disponibles. Trátalo como
                un detalle de diagnóstico, no como un contrato estable.
  responses:
    IPRiskValidationError:
      description: Falló la validación de la solicitud.
      content:
        application/json:
          examples:
            Missing IP address:
              summary: Falta la dirección IP
              value:
                ip_address:
                  - This field is required.
            Invalid IP address:
              summary: Dirección IP no válida
              value:
                ip_address:
                  - Enter a valid IPv4 or IPv6 address.
            Country hint too long:
              summary: Indicación de país demasiado larga
              value:
                claimed_country:
                  - Ensure this field has no more than 3 characters.
    RiskPermissionDenied:
      description: >-
        Falta la clave de API o no es válida, o la organización no puede cubrir
        la consulta en tiempo real. Esta API nunca devuelve `401`.
      content:
        application/json:
          examples:
            Missing or invalid API key:
              summary: >-
                Falta el encabezado `x-api-key`, o la clave no es válida o está
                revocada
              value:
                detail: You do not have permission to perform this action.
            Not enough credits:
              summary: El saldo de la organización no cubre el precio de la consulta
              value:
                error: You don't have enough credits to perform this request.
    RateLimitedWrite:
      description: >-
        Se superó el límite de escritura compartido de 300 solicitudes por
        minuto por clave de API. Respeta `Retry-After` antes de reintentar.
      content:
        application/json:
          schema:
            type: object
            required:
              - detail
            properties:
              detail:
                type: string
          example:
            detail: >-
              Write request rate limit exceeded. You can make up to 300 requests
              per minute.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        La clave de API de tu aplicación, que encontrarás en Developers -> API
        keys en la Business Console. La clave principal tiene acceso completo.
        Una clave con nombre puede tener un alcance limitado: ninguno, lectura o
        escritura por recurso, limitada a algunos flujos o a sesiones aprobadas,
        a una lista de direcciones IP y a una fecha de caducidad. 401 significa
        que la clave es incorrecta, se revocó o expiró; 403 significa que no se
        envió ninguna clave, que la clave no tiene acceso a este recurso o
        acción, o que la solicitud provino de una dirección fuera de su lista de
        IP; 404 en una ruta de sesión significa que la sesión está fuera de los
        flujos o estados de la clave. Una clave sin acceso a contenido
        multimedia recibe las URL de imágenes, videos y PDF como null, y una
        clave sin permiso de escritura en sesiones recibe los enlaces y tokens
        de sesión como null. Consulta https://docs.didit.me/console/api-keys.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.