Developers

GraphQL

Un solo endpoint, POST https://api.datario.cl/graphql, y pides exactamente lo que necesitas. Mismo dato y mismo precio que los demás mecanismos: pagas por el dato, no por el protocolo.

Autenticación

La misma API key en el header Authorization: Bearer (detalle en Empezar). Si falta o no sirve, la respuesta es el 401 HTTP de siempre: la petición ni siquiera llegó al mundo GraphQL. Lo demás responde 200 con errors[] adentro —lo que esperan Apollo, urql y relay— salvo el transporte: 400 si el cuerpo no es JSON con query, 413 si supera el tope y 405 si es GET.

Lo que descuenta

Cada RUT resuelto descuenta su consulta: una query con varios alias descuenta una por cada uno. El costo se calcula ANTES de ejecutar y se cobra completo o no se cobra — si el total no alcanza en plan más saldo, la query no corre y nada se descuenta. Cada respuesta trae extensions.consumo con lo cobrado y lo que queda, desglosado por operación. La tabla completa está en Empezar.

Productos por GraphQL

Una página por producto, con la petición en cada lenguaje y la respuesta real de cada operación. Lo que descuenta cada una es lo mismo por REST, SOAP, GraphQL y MCP: pagas por el dato, no por el protocolo.

ProductoOperaciónDescuenta
Consulta de RUT La ficha por RUT 1 consulta por RUT resuelto
Varios RUT en una query 1 consulta por RUT resuelto
Búsqueda de empresas Búsqueda de empresas 1 consulta por página de resultados
Sociedades y grupos Sociedades y socios, junto a la ficha 2 consultas por RUT resuelto
El grupo, solo 5 consultas por RUT resuelto
Cambios e historial El historial de un RUT, solo 1 consulta por RUT resuelto
Los cambios del padrón entre dos días 1 consulta por elemento devuelto
Vigilancia de RUT Las novedades de tu cartera 1 consulta por elemento devuelto

Errores

200 con errors[] para todo lo posterior al transporte: cada error trae el mismo código y el mismo texto que su gemelo REST, en extensions.code y message. El 404 no existe acá — un RUT válido ausente es data: null, cobrado — y los dos de transporte llevan su estado HTTP a la vista en la lista. Todos viajan con su extensions.consumo — en cero cuando nada se cobró; un error de resolver llega además junto a data y al consumo cobrado de verdad.

  • 200 rut_invalido — El dígito verificador no corresponde al cuerpo. No se cobra: no hubo consulta.
  • 200 rut_invalido — No tiene forma de RUT: cuerpo fuera de 7-8 dígitos, o los ocho caracteres sin guion, que son ambiguos.
  • 200 padron_no_disponible — Nunca pasa en régimen. Solo si la primera carga del padrón no ha corrido.
  • 200 parametros_invalidos — Pediste la búsqueda sin ningún filtro. La API no lista el padrón entero.
  • 200 parametros_invalidos — El texto es tan corto que calzaría con medio padrón. No se cobra: no hubo búsqueda.
  • 200 parametros_invalidos — Ninguna razón social del padrón se acerca a cien caracteres, y un patrón así solo gasta base. No se cobra: no hubo búsqueda.
  • 200 operacion_no_disponible — Esa operación se retiró del catálogo. Como con el mecanismo: cambiar de operación, no reintentar; las vigentes están en /precios.
  • 200 limite_de_velocidad — Fuiste más rápido que las consultas por minuto de tu plan. Retry-After dice cuánto esperar. reintentar_en repite el Retry-After.
  • 200 sin_saldo — El costo TOTAL de la query no alcanza en plan + saldo: no se cobra nada (todo o nada) y consultas_requeridas dice cuánto pedía.
  • 200 saldo_protegido — Hay saldo, pero la cuenta pidió no gastarlo automáticamente: mismo todo-o-nada, con su propio código para no mandar a comprar lo que ya hay.
  • 200 parametros_invalidos — El análogo de limite del REST, con el nombre del argumento GraphQL.
  • 200 parametros_invalidos — El análogo de pagina del REST: el token llegó adulterado o incompleto.
  • 200 parametros_invalidos — Con despues, los filtros van DENTRO del token. Idénticos se aceptan.
  • 200 consulta_invalida — No parsea. Con el mismo código salen «Indica una operación de tipo query (operationName si hay varias).» y «cambios se pide en su propia query: descuenta por cambio devuelto y no se mezcla con campos de costo fijo.»: cambios descuenta por cambio devuelto y va sola en su query.
  • 200 consulta_demasiado_profunda — El selection set anida más de la cuenta (la introspección está exenta).
  • 200 consulta_demasiado_grande — El costo estático supera el tope por request. Parte la query en dos. cambios tiene el suyo, por elementos —100 por request entre todos sus alias— con el mismo código.
  • 400 cuerpo_invalido — El POST no trae un JSON utilizable. Nada se ejecuta ni descuenta.
  • 413 cuerpo_demasiado_grande — El transporte corta antes de parsear. Nada se ejecuta ni descuenta.
200 · errors[] · rut_invalido 200
{
  "errors": [
    {
      "message": "El RUT no es válido: revisa el dígito verificador.",
      "extensions": {
        "code": "rut_invalido"
      }
    }
  ],
  "extensions": {
    "consumo": {
      "consultas_cobradas": 0,
      "ruts_resueltos": 0,
      "paginas_busqueda": 0,
      "plan_restante": null,
      "saldo_restante": null,
      "por_operacion": {}
    }
  }
}

El schema

El SDL completo, con sus descripciones — el mismo que responde la introspección (que es gratis: no descuenta).

"""
El padrón del SII por GraphQL. Mismo dato y mismo precio que REST y SOAP:
cada RUT resuelto descuenta una consulta; cada página de búsqueda, una.
El costo cobrado viaja en `extensions.consumo` de cada respuesta.
"""
type Query {
  """
  La ficha por RUT (toda la base, personas con giro incluidas). Descuenta una
  consulta por RUT resuelto  una query con varios alias descuenta una por
  cada uno. El RUT válido que el SII no registra devuelve null y descuenta
  igual: «no está» también es información. El inválido (dígito verificador)
  no descuenta.
  """
  contribuyente(rut: String!): Contribuyente

  """
  Búsqueda de empresas  SOLO personas jurídicas. Descuenta una consulta por
  página, también la vacía. `despues` es el `pagina_siguiente` de la página
  anterior y lleva los filtros adentro: no los repitas distintos.
  """
  empresas(
    razon_social: String
    actividad: Int
    comuna: String
    region: String
    "Cómo se lee razon_social: contiene (default) o empieza."
    modo: String
    """
    Tramo de ventas mínimo del último año comercial (1..13, la leyenda del
    SII). Refina una búsqueda; solo no la define.
    """
    tramo_minimo: Int
    """
    Trabajadores mínimos del último año comercial. Refina una búsqueda; solo
    no la define.
    """
    trabajadores_minimo: Int
    "true deja solo las empresas sin término de giro ante el SII."
    vigentes: Boolean
    primero: Int = 20
    despues: String
  ): EmpresasConexion!

  """
  Los cambios del padron entre dos dias solo personas juridicas, en el
  orden en que el SII los publico. Descuenta UNA consulta por cambio devuelto,
  minimo una por pagina (tambien la vacia), y por eso `desde` es obligatorio.
  Va SOLA en su query: no se mezcla con contribuyente ni empresas, que cuestan
  fijo. La pagina se lee hasta donde tu plan y tu saldo alcanzan. `despues` es
  el `pagina_siguiente` de la pagina anterior y lleva los filtros adentro.
  """
  cambios(
    "El primer dia, inclusive, en hora de Chile (YYYY-MM-DD). Obligatorio salvo con despues."
    desde: String
    "El ultimo dia, inclusive. Por defecto hoy."
    hasta: String
    "Solo un tipo: alta, baja o cambio."
    tipo: String
    "Solo una parte de la ficha: contribuyente, domicilios, actividades o casilla."
    aspecto: String
    "Solo los cambios de contribuyente en que ese campo difiere."
    campo: String
    comuna: String
    region: String
    actividad: Int
    "Cambios por pagina, 1 a 100."
    primero: Int = 100
    despues: String
  ): CambiosConexion!

  """Las novedades de tu cartera de RUT vigilados (0054), ya entregadas y ya
  descontadas: leerlas NO descuenta. Solo con API key: la consola no las sirve."""
  novedades(
    "Solo las entregadas desde ese dia, inclusive, en hora de Chile (YYYY-MM-DD)."
    desde: String
    "Solo las de esa lista (su id)."
    lista: String
    "Novedades por pagina, 1 a 500."
    primero: Int = 100
    despues: String
  ): NovedadesConexion!
}

"""Todo lo que el padrón sabe de un RUT  la misma ficha del REST."""
type Contribuyente {
  "El RUT que consultaste, normalizado: con guion y sin puntos."
  rut: String!
  "El mismo RUT listo para mostrar: con puntos y guion."
  rut_formateado: String!
  "true si el RUT es de una empresa y false si es de una persona."
  es_empresa: Boolean!
  "El nombre legal, para imprimir en una factura. Vacio cuando el RUT es de una persona."
  razon_social: String!
  "TODOS los giros, con código, descripción, si afecta IVA y desde cuándo."
  actividades: [Actividad!]!
  "Casa matriz y TODAS las sucursales, con sus componentes y desde cuándo rigen."
  domicilios: [Domicilio!]!
  "El correo al que enviarle un DTE. Vacio si no tiene uno registrado."
  casilla_intercambio: String!
  "En que familia cae la sociedad. Vacio si no viene clasificada."
  tipo_sociedad: String!
  "La forma exacta dentro de esa familia. Vacio si no viene clasificada."
  subtipo_sociedad: String!
  """El codigo de tres digitos de ese subtipo. Es contra este que conviene
  comparar si tu logica depende del tipo de sociedad: las dos descripciones son
  texto y pueden cambiar de redaccion. Puede llegar con codigo y con las
  descripciones vacias."""
  cod_subtipo: String!
  """Desde cuando la empresa opera formalmente. ISO 8601 solo fecha
  (1993-01-01). Nula en cerca de la mitad de los RUT."""
  inicio_actividades: String
  """Cuando la empresa cerro su giro. ISO 8601 solo fecha. Nula es el caso
  normal: no confirma que siga operando."""
  termino_giro: String
  "Siempre el mismo texto: de donde salen estos datos."
  fuente: String!
  "Que version de los datos respondio. ISO 8601 con hora y zona, siempre en UTC."
  padron_publicado: String!
  "Cuando se respondio esta llamada. ISO 8601 con hora y zona, siempre en UTC."
  consultado_en: String!
  """El tamano y el regimen del ultimo ano comercial que la nomina anual del SII
  tiene del RUT. Nulo si el SII no lo clasifico como empresa en los dos ultimos
  anos comerciales  que es distinto de un campo vacio adentro."""
  tamano: Tamano
  "true si el RUT esta en la nomina de Grandes Contribuyentes del SII."
  gran_contribuyente: Boolean!
  "El numero de grupo economico de esa nomina. Nulo si no esta en ella."
  grupo_economico: Int
  """
  Sociedades y grupos: en que sociedades participa el RUT, quienes participan
  en el y el agregado de sus personas naturales. Es un BLOQUE con costo
  propio: pedirlo descuenta dos consultas, las mismas que /v1/rut/{rut}/sociedades
   y solo eso si no pides ningun otro campo del contribuyente.
  """
  sociedades: Sociedades
  """
  El grupo que se arma siguiendo las participaciones hasta `saltos` de
  distancia (1 a 3, por defecto 2). Bloque con costo propio: cinco consultas,
  las mismas que /v1/rut/{rut}/grupo.
  """
  grupo(saltos: Int = 2): Grupo
  """
  Que cambio de este RUT en el padron y cuando, del mas reciente al mas
  antiguo. Bloque con costo propio: una consulta, la misma que
  /v1/rut/{rut}/historial.
  """
  historial: Historial
}

"""JSON tal cual: `antes` y `despues` llevan la forma que ese aspecto tiene
en la ficha (un objeto para contribuyente y casilla, la lista completa para
domicilios y actividades)."""
scalar JSON

"""El historial de un RUT: los cambios del padron desde la linea base."""
type Historial {
  "Los cambios del RUT, del mas reciente al mas antiguo. Vacia si no cambio."
  cambios: [Cambio!]!
  """Desde cuando hay historial que contar. ISO 8601 en UTC. Nulo mientras
  ninguna publicacion del SII se haya comparado con otra."""
  historial_desde: String
  fuente: String!
  "De cuando son las nominas vigentes del padron. ISO 8601 en UTC."
  publicado: String!
  consultado_en: String!
}

type Cambio {
  "La publicacion del SII en que aparecio el cambio. ISO 8601 en UTC."
  publicado: String!
  "contribuyente, domicilios, actividades o casilla."
  aspecto: String!
  "alta, baja o cambio."
  tipo: String!
  """En un cambio de contribuyente, que campos difieren entre antes y despues.
  Nulo en los demas."""
  campos: [String!]
  "Como era. Nulo en un alta."
  antes: JSON
  "Como quedo. Nulo en una baja."
  despues: JSON
}

type CambioDelPadron {
  rut: String!
  rut_formateado: String!
  "Su nombre legal hoy segun el padron; en una baja, el que tenia."
  razon_social: String!
  publicado: String!
  aspecto: String!
  tipo: String!
  campos: [String!]
  antes: JSON
  despues: JSON
}

type CambiosConexion {
  "La pagina de cambios, del mas antiguo al mas reciente. Vacia si nada cambio."
  cambios: [CambioDelPadron!]!
  "Si quedan mas cambios despues de esta pagina."
  hay_mas: Boolean!
  """
  El token para pedir la pagina que sigue (argumento `despues`). null si no hay mas 
  o si `hay_mas` es true y esta pagina no se leyo porque otro alias de la misma query
  gasto lo que la cuenta podia pagar: vuelve a pedirla sola, desde el principio.
  """
  pagina_siguiente: String
}

"""Una novedad de la vigilancia (0054): lo que el SII publico sobre un RUT
vigilado, ya entregado y ya descontado."""
type Novedad {
  "Creciente: es por lo que se pagina."
  id: Int!
  rut: String!
  rut_formateado: String!
  razon_social: String!
  "Que cambio, en una frase: la misma del correo y del panel."
  descripcion: String!
  "La publicacion del SII en que aparecio el cambio. ISO 8601 en UTC."
  publicado: String!
  aspecto: String!
  tipo: String!
  campos: [String!]
  antes: JSON
  despues: JSON
  "El nombre de la lista en que se vigila ese RUT."
  lista: String!
  etiqueta: String!
  "Cuando se entrego y se desconto. ISO 8601 en UTC."
  entregada_en: String!
}

type NovedadesConexion {
  "Las novedades, de la mas antigua a la mas nueva desde el cursor."
  novedades: [Novedad!]!
  hay_mas: Boolean!
  "El token para pedir la pagina que sigue (argumento `despues`). null si no hay mas."
  pagina_siguiente: String
  "Cuantas novedades esperan a que la cuenta tenga consultas."
  retenidas: Int!
}

"""Las participaciones de un RUT. `publicado` es la marca de la composicion
de sociedades del SII: otro archivo, con otra fecha que las nominas."""
type Sociedades {
  "En que sociedades participa el RUT."
  participaciones: [Participacion!]!
  "Que personas juridicas participan en el."
  socios: [Participacion!]!
  """El agregado de sus personas naturales: un porcentaje sin cantidad ni
  nombres, que es lo que el SII publica. Nulo si el archivo no trae esa fila."""
  personas_naturales: PersonasNaturales
  fuente: String!
  "De cuando es la composicion de sociedades que respondio. ISO 8601 en UTC."
  publicado: String!
  consultado_en: String!
}

type Participacion {
  rut: String!
  rut_formateado: String!
  razon_social: String!
  "Porcentaje, tal como el SII lo escribe (puede pasar de 100). Nulo si no lo informa."
  participacion: Float
}

type PersonasNaturales {
  participacion: Float
}

"""El grupo de un RUT: las empresas a hasta `saltos` de distancia y las
participaciones entre ellas."""
type Grupo {
  saltos: Int!
  empresas: [EmpresaGrupo!]!
  participaciones: [Arista!]!
  "true si el grupo sigue mas alla del tope de la respuesta."
  truncado: Boolean!
  fuente: String!
  publicado: String!
  consultado_en: String!
}

type EmpresaGrupo {
  rut: String!
  rut_formateado: String!
  razon_social: String!
  "A cuantos saltos del RUT de partida: 1 es participacion directa."
  distancia: Int!
}

type Arista {
  socio: String!
  sociedad: String!
  participacion: Float
}

"""Un tramo de la leyenda del SII: codigo, nombre y rango en UF. hasta_uf es
nulo en el tramo abierto; los dos son nulos en «Sin Informacion» (codigo 1)."""
type Tramo {
  codigo: Int!
  descripcion: String!
  desde_uf: Float
  hasta_uf: Float
}

"""El tramo de capital propio, con el signo aparte: la leyenda es la misma y
negativo dice si el capital es negativo."""
type TramoCapital {
  codigo: Int!
  descripcion: String!
  desde_uf: Float
  hasta_uf: Float
  negativo: Boolean!
}

"""El regimen tributario: el codigo es el que conviene comparar (14A, 14D,
14D8, no14, otros); la descripcion es el texto del SII."""
type Regimen {
  codigo: String!
  descripcion: String!
}

"""El rubro: la letra de la seccion CIIU y el texto del SII."""
type Rubro {
  codigo: String!
  descripcion: String!
}

"""Lo que la nomina anual de empresas del SII dice del RUT en su ultimo ano
comercial: la misma forma que `tamano` en la ficha del REST."""
type Tamano {
  "El ano comercial declarado. Un ENTERO, no una fecha."
  anio_comercial: Int!
  "El tramo de ventas anuales, en UF. Codigo 1 es «Sin Informacion»."
  tramo_ventas: Tramo
  "Trabajadores dependientes informados. 0 en cerca de la mitad de las empresas."
  trabajadores: Int!
  "El tramo de capital propio. Nulo si no consta."
  tramo_capital: TramoCapital
  "El regimen tributario. Nulo si no consta."
  regimen: Regimen
  "true si tributa por renta presunta."
  renta_presunta: Boolean!
  "El rubro. Nulo si el SII no lo informo."
  rubro: Rubro
  "El subrubro, tal como lo escribe el SII. Nulo si no lo informo."
  subrubro: String
  """El codigo de la actividad principal, el mismo `codigo` de actividades.
  Nulo si el SII no la informo o no se pudo resolver."""
  actividad_principal: Int
  "Primera inscripcion de actividades. ISO 8601 solo fecha. Nula si no consta."
  primera_inscripcion: String
  "La comuna segun la nomina anual."
  comuna: String!
}

type Actividad {
  codigo: Int!
  descripcion: String!
  afecta_iva: Boolean!
  "Desde cuando tiene este giro. ISO 8601 solo fecha (1993-01-01). Nula si no consta."
  desde: String
}

type Domicilio {
  "matriz o sucursal."
  tipo: String!
  "La direccion ya compuesta, lista para imprimir. NO incluye bloque ni villa."
  direccion: String!
  calle: String!
  numero: String!
  departamento: String!
  comuna: String!
  ciudad: String!
  region: String!
  bloque: String!
  "Villa o poblacion."
  villa: String!
  "Desde cuando rige esta direccion. ISO 8601 solo fecha. Nula si no consta."
  vigente_desde: String
}

"""Una página de resultados de búsqueda."""
type EmpresasConexion {
  "La página de coincidencias. Vacía si no hay: eso también es respuesta."
  resultados: [EmpresaResumen!]!
  "Si quedan más resultados después de esta página."
  hay_mas: Boolean!
  "El token para pedir la página que sigue (argumento `despues`). null si no hay más."
  pagina_siguiente: String
  """
  Si el texto parece tener un error de tipeo y hay una forma que  está en el
  padrón, viene acá (por ejemplo «FARMACIA» para «FARMASIA»); null si no. Puede
  venir aunque haya resultados. Se pide como una búsqueda nueva con ese
  `razon_social`, y esa página descuenta su consulta como cualquier otra.
  """
  sugerencia: String
}

"""Lo justo para elegir y pedir la ficha por RUT."""
type EmpresaResumen {
  rut: String!
  rut_formateado: String!
  razon_social: String!
  "La comuna de la casa matriz."
  comuna: String!
  "La región de la casa matriz."
  region: String!
  "El tramo de ventas del último año comercial (1..13). Nulo si la nómina anual no lo trae."
  tramo_ventas: Int
  "Los trabajadores del último año comercial. Nulo si la nómina anual no lo trae."
  trabajadores: Int
}

Probar en la consola Descargar colección Postman REST