Mantener sus sistemas externos sincronizados con los datos de Zendesk puede parecer una tarea interminable. Ya sea que esté construyendo un almacén de datos, ejecutando análisis o sincronizando información de tickets con su CRM, necesita una forma confiable de obtener solo lo que ha cambiado desde su última actualización. La exportación incremental de la API de Zendesk está diseñada exactamente para este propósito.
A diferencia de los puntos finales (endpoints) de la API estándar diseñados para consultas en tiempo real, las exportaciones incrementales están diseñadas específicamente para la sincronización masiva de datos. Le permiten obtener registros que se crearon o actualizaron desde un punto específico en el tiempo, lo que los hace ideales para canalizaciones (pipelines) ETL y sincronizaciones de datos regulares.
En esta guía, aprenderá a utilizar la API de exportación incremental de Zendesk de forma eficaz. Cubriremos los dos enfoques de paginación (basado en cursor y basado en tiempo), repasaremos una implementación completa en Python y compartiremos las mejores prácticas para las canalizaciones de datos de producción. Si está buscando una manera de obtener análisis de Zendesk sin construir una infraestructura personalizada, eesel AI ofrece integraciones de Zendesk nativas que manejan la sincronización de datos automáticamente.
¿Qué es la API de exportación incremental de Zendesk?
La API de exportación incremental es un conjunto de puntos finales diseñados para la exportación masiva de datos en lugar de la búsqueda de registros individuales. Si bien la API estándar de Tickets es excelente para obtener un ticket específico o buscar con filtros, no está optimizada para sincronizar grandes conjuntos de datos de manera eficiente.
Así es como funcionan las exportaciones incrementales: usted proporciona una hora de inicio (como una marca de tiempo de la época de Unix), y la API devuelve todos los registros creados o actualizados desde ese momento. En su próxima sincronización, utiliza la hora de finalización o el cursor de la respuesta anterior como su nuevo punto de inicio. Esto crea un bucle de sincronización eficiente donde solo obtiene los datos modificados.
La API admite dos métodos de paginación:
- La paginación basada en cursor utiliza un puntero de cursor opaco para rastrear la posición. Es más consistente, elimina duplicados y es el enfoque recomendado cuando está disponible.
- La paginación basada en tiempo utiliza marcas de tiempo de inicio y fin. Es compatible con todos los puntos finales incrementales, pero puede devolver registros duplicados cuando varios elementos comparten la misma marca de tiempo.
Puede exportar incrementalmente tickets, usuarios, organizaciones, eventos de tickets, datos de llamadas de Talk, conversaciones de Chat, artículos del Centro de ayuda y registros de objetos personalizados. Para los equipos que construyen análisis integrales, nuestra guía sobre la API de tickets de Zendesk cubre puntos finales adicionales que complementan las exportaciones incrementales.
Cuándo utilizar exportaciones incrementales frente a la API de búsqueda
Una pregunta común es si utilizar exportaciones incrementales o la API de búsqueda para obtener datos de tickets. La diferencia clave es el propósito: las exportaciones incrementales están diseñadas para la sincronización de datos, mientras que la búsqueda está diseñada para la consulta.
Utilice exportaciones incrementales cuando:
- Esté sincronizando datos con un almacén de datos o un sistema externo
- Necesite procesar cada cambio de ticket de forma fiable
- Esté construyendo canalizaciones de análisis o informes
- Desee minimizar las llamadas a la API obteniendo solo los cambios
Utilice la API de búsqueda cuando:
- Necesite encontrar tickets específicos que coincidan con criterios complejos
- Esté construyendo una interfaz de búsqueda para agentes
- Necesite resultados en tiempo real con filtrado avanzado
Paginación basada en cursor frente a basada en tiempo: ¿Cuál debe utilizar?
Zendesk ofrece ambos métodos de paginación, pero el basado en cursor es el claro ganador cuando está disponible. Así es como difieren.
Paginación basada en cursor (recomendada)
Las exportaciones basadas en cursor utilizan un puntero opaco (el cursor) para rastrear su posición en el conjunto de datos. Después de su solicitud inicial con un start_time, las solicitudes posteriores utilizan el parámetro cursor.
Beneficios clave:
- Sin registros duplicados, incluso cuando los elementos comparten marcas de tiempo
- Tiempos de respuesta y tamaños de carga útil más consistentes
- Mejor rendimiento para grandes conjuntos de datos
- Límites de velocidad más altos (10 solicitudes por minuto para tickets, 20 para usuarios o 60 con el complemento High Volume API)
Recursos admitidos:
- Tickets
- Usuarios
- Registros de objetos personalizados
Cómo funciona:
- Realice la solicitud inicial con el parámetro
start_time - Extraiga
after_cursorde la respuesta - Utilice el parámetro
cursorpara la siguiente solicitud - Repita hasta que
end_of_streamsea verdadero
Paginación basada en tiempo
Las exportaciones basadas en tiempo utilizan marcas de tiempo de la época de Unix para definir su ventana de consulta. Cada respuesta incluye un end_time que utiliza como start_time para su próxima solicitud.
Limitaciones:
- Puede devolver registros duplicados cuando varios elementos comparten la misma marca de tiempo
- Rendimiento menos consistente
- Límites de velocidad más bajos (10 solicitudes por minuto)
Cuándo utilizarlo:
- Cuando el basado en cursor no está disponible (eventos de tickets, organizaciones, datos de Talk)
- Para scripts simples donde el manejo de duplicados no es crítico
Matriz de decisión
| Factor | Basado en cursor | Basado en tiempo |
|---|---|---|
| Manejo de duplicados | Ninguno (único garantizado) | Debe eliminar duplicados manualmente |
| Rendimiento | Consistente | Variable |
| Límite de velocidad | 10-20/min (60 con complemento) | 10/min |
| Disponible para | Tickets, usuarios, objetos personalizados | Todos los recursos |
Utilice el basado en cursor siempre que sea posible. La única vez que debe utilizar el basado en tiempo es cuando el basado en cursor no está disponible para su tipo de recurso.
Primeros pasos: Autenticación y requisitos previos
Antes de empezar a codificar, necesitará algunas cosas configuradas.
Requerido:
- Una cuenta de Zendesk con privilegios de administrador
- Un token de API (genere uno en Centro de administración > Aplicaciones e integraciones > API > API de Zendesk)
- Python 3.7+ instalado
Paquetes de Python:
pip install requests python-dotenv
Configuración del entorno:
Cree un archivo .env para almacenar sus credenciales de forma segura:
ZENDESK_SUBDOMAIN=su-subdominio
ZENDESK_EMAIL=su-correo-electronico@empresa.com
ZENDESK_API_TOKEN=su-token-de-api
La API de exportación incremental utiliza la autenticación básica. Pasará su dirección de correo electrónico combinada con /token como nombre de usuario, y su token de API como contraseña.
Paso a paso: Exportación de tickets con paginación basada en cursor
Repasemos una implementación completa para exportar tickets utilizando la paginación basada en cursor. Este patrón maneja la paginación, la limitación de velocidad y la recuperación de errores.
El código
import os
import time
import requests
from requests.auth import HTTPBasicAuth
from dotenv import load_dotenv
load_dotenv()
class ZendeskIncrementalExport:
def __init__(self):
self.subdomain = os.getenv('ZENDESK_SUBDOMAIN')
self.email = os.getenv('ZENDESK_EMAIL')
self.api_token = os.getenv('ZENDESK_API_TOKEN')
self.base_url = f"https://{self.subdomain}.zendesk.com/api/v2"
self.auth = HTTPBasicAuth(f"{self.email}/token", self.api_token)
def export_tickets(self, start_time=None, cursor=None):
"""
Export tickets using cursor-based pagination.
Args:
start_time: Unix timestamp for initial export (required for first call)
cursor: Cursor from previous response (for subsequent calls)
"""
url = f"{self.base_url}/incremental/tickets/cursor.json"
params = {}
if cursor:
params['cursor'] = cursor
elif start_time:
params['start_time'] = start_time
else:
raise ValueError("Either start_time or cursor must be provided")
try:
response = requests.get(url, auth=self.auth, params=params, timeout=30)
if response.status_code == 429:
# Rate limited - implement exponential backoff
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
return self.export_tickets(start_time, cursor)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
raise
def full_export(self, start_time):
"""
Perform a complete export, handling pagination automatically.
"""
all_tickets = []
cursor = None
page_count = 0
while True:
data = self.export_tickets(
start_time=start_time if cursor is None else None,
cursor=cursor
)
tickets = data.get('tickets', [])
all_tickets.extend(tickets)
page_count += 1
print(f"Fetched page {page_count}: {len(tickets)} tickets")
# Check if we've reached the end
if data.get('end_of_stream'):
print(f"Export complete. Total tickets: {len(all_tickets)}")
return {
'tickets': all_tickets,
'final_cursor': data.get('after_cursor'),
'pages': page_count
}
cursor = data.get('after_cursor')
# Respect rate limits - sleep between requests
time.sleep(3) # 20 requests/min = 1 request per 3 seconds
if __name__ == "__main__":
exporter = ZendeskIncrementalExport()
# Start from 24 hours ago
import datetime
start_time = int((datetime.datetime.now() - datetime.timedelta(days=1)).timestamp())
result = exporter.full_export(start_time)
# Save the final cursor for next sync
print(f"Save this cursor for next run: {result['final_cursor']}")
Detalles clave de la implementación
La ventana de exclusión de 1 minuto: La API excluye los datos del minuto más reciente para evitar condiciones de carrera. Su end_time nunca será más reciente que hace un minuto. Planifique su programa de sincronización en consecuencia.
Manejo del límite de velocidad: El código implementa una retirada exponencial cuando se recibe una respuesta 429. El encabezado Retry-After le indica exactamente cuánto tiempo debe esperar.
Persistencia del cursor: Guarde siempre el cursor final de forma atómica con sus escrituras de datos. Si su script falla después de escribir datos pero antes de guardar el cursor, procesará registros duplicados en la siguiente ejecución.
Exclusión de tickets eliminados: Agregue exclude_deleted=true a los parámetros de su solicitud si no desea tickets eliminados en su exportación. Los tickets eliminados se conservan durante 90 días después de la eliminación permanente, por lo que aparecerán en las exportaciones a menos que se excluyan.
Trabajar con otros puntos finales de exportación incremental
Los mismos patrones se aplican a otros recursos de Zendesk, aunque las URL de los puntos finales y los métodos de paginación disponibles varían.
Usuarios y organizaciones
Estos siguen el mismo patrón que los tickets:
url = f"{base_url}/incremental/users/cursor.json"
url = f"{base_url}/incremental/organizations.json"
Eventos de tickets
Los eventos de tickets son solo basados en tiempo e incluyen un registro de cada cambio realizado en los tickets:
url = f"{base_url}/incremental/ticket_events.json"
params = {'start_time': start_time, 'include': 'comment_events'}
La carga lateral comment_events es particularmente útil si necesita el texto real del comentario, no solo los metadatos sobre los cambios.
Datos de llamadas de Talk
Exporte registros de llamadas y tramos de llamadas para análisis de voz:
url = f"{base_url}/channels/voice/stats/incremental/calls.json"
url = f"{base_url}/channels/voice/stats/incremental/legs.json"
Límite de velocidad: 10 solicitudes por minuto para los puntos finales de Talk.
Datos de chat
Las exportaciones de chat utilizan microsegundos en lugar de segundos para las marcas de tiempo:
url = f"{base_url}/chat/incremental/chats.json"
start_time_micro = start_time * 1000000
url = f"{base_url}/chat/incremental/agent_timeline.json?start_time={start_time_micro}"
Nota: La API de chat requiere que la hora de inicio sea al menos 5 minutos en el pasado.
Artículos del Centro de ayuda
Exporte los cambios de metadatos de los artículos:
url = f"{base_url}/help_center/incremental/articles.json"
Devuelve hasta 1000 artículos por página. La URL next_page contiene una nueva hora de inicio basada en la marca de tiempo de la última actualización del artículo.
Manejo de límites de velocidad y errores
Las canalizaciones de datos de producción necesitan un manejo de errores robusto. Esto es lo que debe vigilar.
Encabezados de límite de velocidad
Los puntos finales basados en cursor devuelven información detallada sobre el límite de velocidad:
Zendesk-RateLimit-incremental-exports-cursor: total=20; remaining=15; resets=45
Analice estos encabezados para limitar proactivamente sus solicitudes en lugar de esperar respuestas 429.
Estrategia de retirada exponencial
Cuando alcance un límite de velocidad, utilice una retirada exponencial con fluctuación:
import random
def backoff_with_jitter(attempt, base_delay=3):
"""Calculate delay with exponential backoff and jitter."""
delay = min(base_delay * (2 ** attempt), 60) # Cap at 60 seconds
jitter = random.uniform(0, delay * 0.1) # Add 0-10% jitter
return delay + jitter
for attempt in range(5):
try:
response = requests.get(url, auth=auth)
if response.status_code == 429:
delay = backoff_with_jitter(attempt)
time.sleep(delay)
continue
response.raise_for_status()
break
except requests.exceptions.RequestException:
if attempt == 4: # Last attempt
raise
delay = backoff_with_jitter(attempt)
time.sleep(delay)
Errores comunes y soluciones
| Error | Causa | Solución |
|---|---|---|
| 401 No autorizado | Credenciales no válidas | Compruebe el formato del correo electrónico (debe incluir /token) y el token de API |
| 403 Prohibido | Permisos insuficientes | Asegúrese de que la cuenta tenga acceso de administrador |
| 422 No procesable | start_time no válido | Verifique que la marca de tiempo tenga al menos 60 segundos en el pasado |
| 429 Demasiadas solicitudes | Límite de velocidad excedido | Implemente la retirada y respete el encabezado Retry-After |
| 500/502/503 | Error del servidor de Zendesk | Vuelva a intentar con una retirada exponencial |
Pruebas con exportaciones de muestra
Zendesk proporciona un punto final de exportación de muestra con límites más estrictos (10 solicitudes por cada 20 minutos) pero respuestas más pequeñas. Utilice esto para el desarrollo y las pruebas:
url = f"{base_url}/incremental/tickets/sample.json?start_time={start_time}"
Construcción de una canalización de datos de producción
Para el uso en producción, querrá una arquitectura más robusta que un simple script.
Arquitectura recomendada
Trabajo programado (Airflow/Lambda/Cron)
↓
API de exportación incremental
↓
Validación y transformación de datos
↓
Almacén de datos (Snowflake/BigQuery/Redshift)
↓
Panel de análisis
Almacenamiento del estado del cursor
Nunca confíe en archivos locales para el almacenamiento del cursor en producción. Utilice un almacenamiento persistente:
import psycopg2
def save_cursor(cursor, last_sync_time):
conn = psycopg2.connect(database_url)
cur = conn.cursor()
cur.execute("""
INSERT INTO zendesk_sync_state (cursor, last_sync_time, updated_at)
VALUES (%s, %s, NOW())
ON CONFLICT (id) DO UPDATE SET
cursor = EXCLUDED.cursor,
last_sync_time = EXCLUDED.last_sync_time,
updated_at = EXCLUDED.updated_at
""", (cursor, last_sync_time))
conn.commit()
Eliminación de duplicados para exportaciones basadas en tiempo
Si está utilizando la paginación basada en tiempo, implemente la eliminación de duplicados:
def deduplicate_records(records, key_fields):
"""
Remove duplicates based on composite key.
For tickets: (id, updated_at)
For ticket events: (id, created_at)
"""
seen = set()
unique = []
for record in records:
key = tuple(record.get(f) for f in key_fields)
if key not in seen:
seen.add(key)
unique.append(record)
return unique
tickets = deduplicate_records(tickets, ['id', 'updated_at'])
Monitoreo y alertas
Realice un seguimiento de estas métricas en su canalización:
- Duración de la sincronización y recuentos de registros
- Impactos en el límite de velocidad y recuentos de reintentos
- Solicitudes fallidas y tasas de error
- Actualidad de los datos (tiempo desde la última sincronización exitosa)
Configure alertas para:
- Fallas de sincronización o reintentos excesivos
- Recuentos de registros inusualmente bajos (posibles problemas de la API)
- Actualidad de los datos que exceda su SLA
Alternativa: Soluciones gestionadas
Construir y mantener una canalización de datos requiere recursos de ingeniería. Si su equipo necesita análisis de Zendesk sin la sobrecarga de la infraestructura, el Agente de IA de eesel AI proporciona análisis, informes e información automatizados de tickets directamente desde sus datos de Zendesk, sin necesidad de una canalización personalizada.

Limitaciones y mejores prácticas clave
Antes de implementar en producción, tenga en cuenta estos puntos.
Mejores prácticas
- Utilice siempre la paginación basada en cursor cuando esté disponible. Los beneficios de rendimiento y consistencia valen la pena.
- Almacene los cursores de forma atómica con las escrituras de datos. Utilice una transacción para asegurarse de que el cursor y los datos estén siempre sincronizados.
- Maneje la ventana de exclusión de 1 minuto. No espere datos más recientes que hace un minuto.
- Implemente escrituras idempotentes. Diseñe su sistema de destino para manejar los registros duplicados con elegancia.
- Supervise los encabezados de límite de velocidad. La limitación proactiva supera a la retirada reactiva.
Limitaciones que debe conocer
- Retención de tickets eliminados: Los tickets eliminados permanecen en las exportaciones durante aproximadamente 120 días en total (30 días hasta la eliminación permanente, luego 90 días después de la limpieza).
- Tickets archivados: Los tickets archivados por Zendesk no se incluyen en las exportaciones incrementales.
- Datos limpiados: Después de 30 días, los tickets eliminados tienen su contenido limpiado (reemplazado con "SCRUBBED" o "X").
- Sin garantías en tiempo real: La API está diseñada para sincronizaciones por lotes, no para transmisión en tiempo real.
Empiece a sincronizar sus datos de Zendesk de forma eficiente
La exportación incremental de la API de Zendesk le brinda una forma confiable de mantener los sistemas externos sincronizados con sus datos de soporte. Al utilizar la paginación basada en cursor, manejar los límites de velocidad con elegancia y almacenar el estado correctamente, puede construir canalizaciones de datos robustas que se escalen con su volumen de tickets.
Puntos clave:
- Utilice la paginación basada en cursor para tickets y usuarios cuando sea posible
- Implemente la retirada exponencial para el manejo del límite de velocidad
- Almacene los cursores de forma atómica con sus escrituras de datos
- Tenga en cuenta la ventana de exclusión de datos de 1 minuto en su programa de sincronización
Construir una canalización personalizada tiene sentido cuando tiene necesidades específicas de transformación de datos o se está integrando con sistemas propietarios. Pero si está buscando principalmente análisis, informes e información impulsada por IA de sus datos de Zendesk, considere si una solución gestionada como eesel AI podría ahorrarle a su equipo meses de esfuerzo de ingeniería. Nuestra integración de Zendesk maneja la sincronización de datos automáticamente, brindándole acceso inmediato a los análisis de tickets sin escribir una sola línea de código de API.

Preguntas frecuentes
Compartir esta entrada

Article by
Stevia Putri
Stevia Putri is a marketing generalist at eesel AI, where she helps turn powerful AI tools into stories that resonate. She’s driven by curiosity, clarity, and the human side of technology.



