Примеры использования API

Готовые рецепты на Curl, Python и JavaScript для запроса общедоступных данных, синхронизации базы данных, отправки показаний яйцеловки и кварталы посещений.

/ru-ru/apiВернуться к ссылке

Первые шаги

Все вызовы поступают из одной базы данных и возвращают JSON. Публичные конечные точки не требуют аутентификации; частные требуют вашего ключа доступа.

Базовый адрес

/ru-ru/api

Префикс языка является частью URL-адреса (ru-ru), но возвращаемые данные это не меняет — записи поступают из банка именно такими, какими они были прописаны в поле.

Быстрый тест
curl -G -d "municipality=Ponta Pora" --data-urlencode "country=Brasil" \
  /ru-ru/api/lastcountingpublic

Если ответ представляет собой список JSON, интеграция уже работает.

Ключ находится в URL-адресе. На всех частных конечных точках, включая POST, key читается из строки запроса (?key=SUA_CHAVE). Отправка его только в теле формы возвращает результат 404 "Wrong key". Остальные поля продолжают появляться в тексте запроса.

Ознакомьтесь с общедоступными данными

Конечная точка /api/lastcountingpublic возвращает последние данные о количестве яиц с указанием эпидемиологической широты, долготы, недели и года. Это отправная точка для карт, панелей и исследований.

GET/api/lastcountingpublicОбщественный
curl
curl -G \
  --data-urlencode "municipality=Ponta Pora" \
  --data-urlencode "date_start=2025-01-01" \
  --data-urlencode "date_end=2025-12-31" \
  /ru-ru/api/lastcountingpublic
Python
import requests

BASE = "/ru-ru/api"

resposta = requests.get(
    BASE + "/lastcountingpublic",
    params={
        "municipality": "Ponta Pora",
        "date_start": "2025-01-01",
        "date_end": "2025-12-31",
    },
    timeout=60,
)
resposta.raise_for_status()

for contagem in resposta.json():
    print(contagem["date"], contagem["ovitrap_id"], contagem["eggs"])
JavaScript
const BASE = "/ru-ru/api";

const params = new URLSearchParams({
    state: "MS",
    date_start: "2025-01-01",
});

const resposta = await fetch(BASE + "/lastcountingpublic?" + params);
const contagens = await resposta.json();

console.log(contagens.length, "contagens");

Те же параметры местоположения (country, state, municipality) Они применяются к другим общедоступным конечным точкам: яйцеловкам, кварталам, EDL, обслуживанию EDL и стратегическим точкам. Простое изменение пути к конечной точке уже меняет набор данных.

Без каких-либо параметров местоположения конечная точка возвращает данные для всей Бразилии. Фильтрация по муниципалитету или штату значительно ускоряет ответ.

Просмотреть все страницы

Поддержка конечных точек с разбиением на страницы page от 1 до 100. Выше этого API отвечает текстом "Maximum pagination is 100" и статус 200 — то есть тело уже не список. Всегда проверяйте тип перед итерацией.

Python
import requests

BASE = "/ru-ru/api"


def baixar_tudo(endpoint, **filtros):
    """Percorre as paginas ate a API devolver uma pagina vazia."""
    registros = []

    for pagina in range(1, 101):
        resposta = requests.get(
            BASE + endpoint,
            params=dict(filtros, page=pagina),
            timeout=60,
        )
        resposta.raise_for_status()
        dados = resposta.json()

        # limite de paginacao atingido: a API devolve uma string, nao uma lista
        if not isinstance(dados, list):
            print("Aviso:", dados)
            break

        if not dados:
            break

        registros.extend(dados)

    return registros


ovitrampas = baixar_tudo("/getmunicipalityovitrapspublic", municipality="Ponta Pora")
print(len(ovitrampas), "ovitrampas")
Вы добрались до 100-й страницы? Сузьте фильтр вместо того, чтобы пытаться просмотреть страницу 101: просмотрите округ за округом или используйте date_start e date_end разделить период на более мелкие интервалы.

Дополнительная синхронизация

Чтобы поддерживать зеркальную базу, не загружайте все заново при каждом запуске. Конечная точка /api/lastcountingpublic принял id, который возвращает только счетчики этого идентификатора, а также date (дата включения) e date_collect (data de coleta).

Python
import json
import os
import requests

BASE = "/ru-ru/api"
ESTADO = "ultimo_id.json"


def ultimo_id_lido():
    if os.path.exists(ESTADO):
        with open(ESTADO) as arquivo:
            return json.load(arquivo)["counting_id"]
    return 0


def sincronizar():
    ultimo = ultimo_id_lido()
    novas = []

    for pagina in range(1, 101):
        resposta = requests.get(
            BASE + "/lastcountingpublic",
            params={"municipality": "Ponta Pora", "id": ultimo, "page": pagina},
            timeout=60,
        )
        resposta.raise_for_status()
        dados = resposta.json()

        if not isinstance(dados, list) or not dados:
            break

        novas.extend(dados)

    if novas:
        maior = max(item["counting_id"] for item in novas)
        with open(ESTADO, "w") as arquivo:
            json.dump({"counting_id": maior}, arquivo)

    return novas


print(len(sincronizar()), "contagens novas")

Всегда держите самый большой counting_id получено, а не дата исполнения: счетчик, введенный в поле сегодня, может относиться к предыдущей неделе, и фильтр по идентификатору не позволяет этим записям ускользнуть.

Экспорт в CSV

Публичные счетчики уже имеют координаты, поэтому вы можете создать электронную таблицу или загрузить карту без каких-либо дополнительных объединений.

Python
import csv
import requests

BASE = "/ru-ru/api"

COLUNAS = [
    "counting_id",
    "municipality",
    "state_code",
    "ovitrap_id",
    "latitude",
    "longitude",
    "week",
    "year",
    "eggs",
    "date",
    "date_collect",
]

resposta = requests.get(
    BASE + "/lastcountingpublic",
    params={"state": "MS", "date_start": "2025-01-01"},
    timeout=60,
)
contagens = resposta.json()

with open("contagens.csv", "w", newline="", encoding="utf-8") as arquivo:
    escritor = csv.DictWriter(arquivo, fieldnames=COLUNAS, extrasaction="ignore")
    escritor.writeheader()
    escritor.writerows(contagens)

print("Arquivo contagens.csv gerado com", len(contagens), "linhas")

Отправка данных

В следующих примерах используется частный API и требуется ключ. Географический охват ключа определяет, что он может записывать — муниципальный ключ записывает только в самом муниципалитете.

POST/api/postcountingЧастный

Отправить показания существующей яйцеловки

Отправляет количество яиц уже зарегистрированной яйцеловки. Яйцеловушка расположена рядом с ovitrap_group_id в пределах муниципалитета ключа, а эпидемиологическая неделя рассчитывается по полю date.

curl
curl -X POST \
  -d "ovitrap_group_id=97" \
  -d "ovitrap_lat=-7.000000" \
  -d "ovitrap_lng=-8.000000" \
  -d "date=2025-01-20" \
  -d "counting_observation_id=1" \
  -d "counting_eggs=5" \
  "/ru-ru/api/postcounting?key=SUA_CHAVE"
Python
import requests

BASE = "/ru-ru/api"
CHAVE = "SUA_CHAVE"

resposta = requests.post(
    BASE + "/postcounting",
    params={"key": CHAVE},          # a chave vai na URL
    data={                          # os dados vao no corpo
        "ovitrap_group_id": 97,
        "ovitrap_lat": -7.000000,
        "ovitrap_lng": -8.000000,
        "date": "2025-01-20",
        "counting_observation_id": 1,
        "counting_eggs": 5,
    },
    timeout=60,
)

print(resposta.status_code, resposta.json())
# 200 "Contagem registrada"
Внимание: Если отправленные координаты отличаются от зарегистрированных, яйцеловушка обновляется с учетом новой позиции. Всегда отправляйте реальную координату точки, а не координату агента на момент считывания.
POST/api/postcountingЧастный

Установите новую яйцеловушку рядом с местом чтения.

Если ovitrap_group_id в муниципалитете пока не существует, яйцеловушка создается по тому же заказу. Также отправьте адрес и, если применимо, тип (ovitrap_type_id: 1 городской, узор; 2 rural).

Python
import requests

BASE = "/ru-ru/api"
CHAVE = "SUA_CHAVE"

resposta = requests.post(
    BASE + "/postcounting",
    params={"key": CHAVE},
    data={
        "ovitrap_group_id": 96,
        "ovitrap_address_district": "Centro",
        "ovitrap_address_street": "Rua das Flores",
        "ovitrap_address_number": "123",
        "ovitrap_address_complement": "",
        "ovitrap_address_sector": "Setor 04",
        "ovitrap_responsable": "Maria Souza",
        "ovitrap_block_id": "12",
        "ovitrap_type_id": 2,          # 1 = urbana (padrao), 2 = rural
        "ovitrap_lat": -7.000000,
        "ovitrap_lng": -8.000000,
        "date": "2025-01-20",
        "counting_date_collect": "2025-01-27",
        "counting_observation_id": 1,
        "counting_eggs": 5,
    },
    timeout=60,
)

print(resposta.status_code, resposta.json())
Конкретные ответы:
  • 400 — недостаток ovitrap_lat, ovitrap_lng ou ovitrap_group_id.
  • 400ovitrap_type_id отличается от 1 или 2.
  • 404 — Этой яйцеловке уже есть счет, год и неделя.
  • 500 — дата отправки приходится на будущую или недействительную эпидемиологическую неделю.
POST/api/postactionЧастный

Зарегистрируйте посещение квартала

Использовать block_id запустить посещение в уже зарегистрированном квартале. Если квартал еще не существует, отправьте block_group_id с данными квартала и автоматически создается в ключевом муниципалитете.

Python — существующий квартал
import requests

BASE = "/ru-ru/api"
CHAVE = "SUA_CHAVE"

visita = {
    "block_id": 97,
    "date": "2025-01-20",

    "block_land_residence": 10,
    "action_land_residence_out": 2,
    "action_land_residence_breedings": 1,
    "action_land_residence_treated": 1,

    "action_deposit_a1_quantity": 4,
    "action_deposit_a1_eliminated": 2,
    "action_deposit_a1_treated": 1,
    "action_deposit_a1_larvicid": 10,

    "action_observation": "Foco encontrado em pneus nos fundos do imovel.",
}

resposta = requests.post(
    BASE + "/postaction", params={"key": CHAVE}, data=visita, timeout=60
)

if resposta.status_code == 409:
    print("Ja existe visita para esse quarteirao nessa semana")
else:
    print(resposta.status_code, resposta.json())
Python — создание квартала
visita = {
    "block_group_id": 97,
    "date": "2025-01-20",
    "block_address_district": "Centro Historico",
    "block_address_sector": "Setor 04 - Norte",
    "block_coordinates": "[[-7.123, -34.845], [-7.124, -34.846]]",
    "block_lat": -7.123456,
    "block_lng": -34.845678,
    "block_responsable": "Joao da Silva",

    "block_land_residence": 10,
    "action_land_residence_out": 2,
    "action_land_residence_breedings": 1,
    "action_land_residence_treated": 1,

    "action_observation": "Area com alta densidade de recipientes descartaveis.",
}

resposta = requests.post(
    BASE + "/postaction", params={"key": CHAVE}, data=visita, timeout=60
)
print(resposta.status_code, resposta.json())

Полный список полей недвижимости и депозитов (a1, a2, b, c, d1, d2, e) находится в ссылка на конечную точку. Неотправленные числовые поля предполагают ноль, но любое значение, не являющееся числом, возвращает 400.

Удалить записи

Удаление является окончательным и всегда ограничивается ключевым муниципалитетом.

curl
# leitura de uma ovitrampa (identificada pela ovitrampa + data)
curl -X POST -d "ovitrap_group_id=97" -d "date=2025-01-20" \
  "/ru-ru/api/postdeletecounting?key=SUA_CHAVE"

# a ovitrampa inteira
curl -X POST -d "ovitrap_group_id=97" \
  "/ru-ru/api/postdeleteovitrap?key=SUA_CHAVE"

# visita de um quarteirao (quarteirao + data)
curl -X POST -d "block_group_id=97" -d "date=2025-01-20" \
  "/ru-ru/api/postdeleteaction?key=SUA_CHAVE"

# o quarteirao inteiro
curl -X POST -d "block_group_id=97" \
  "/ru-ru/api/postdeleteblock?key=SUA_CHAVE"
Чтобы исправить счетчик, указанный с неправильным значением, удалите показания с помощью /api/postdeletecounting и отправьте его снова с помощью /api/postcounting — повторная отправка возвращает ошибку дублирования.

Обработка ошибок

Тело ответа всегда представляет собой строку JSON с сообщением. Стоит разобраться в каждом случае, ведь не каждая ошибка заслуживает повторной попытки:

СитуацияКодЧто делать
"Wrong key"404Убедитесь, что ключ находится в строке запроса, а не в теле.
Двойной счет404На этой неделе уже есть показания для яйцеловки; пожалуйста, удалите перед повторной отправкой.
Повторное посещение409На этой неделе уже запланировано посещение квартала.
Вне области действия403Квартал не принадлежит муниципалитету Чаве. Не повторяйте.
Отсутствует обязательное поле400Исправить подачу; повторение возвращает ту же ошибку.
Неверная неделя или год.500Дата выходит за рамки принятой эпидемиологической недели. Исправьте поле date.
Python
import time
import requests

NAO_REPETIR = {400, 403, 404, 409}


def enviar(url, chave, dados, tentativas=3):
    for tentativa in range(tentativas):
        try:
            resposta = requests.post(
                url, params={"key": chave}, data=dados, timeout=60
            )
        except requests.RequestException as erro:
            print("Falha de rede:", erro)
            time.sleep(5 * (tentativa + 1))
            continue

        if resposta.status_code in NAO_REPETIR:
            print("Erro definitivo:", resposta.status_code, resposta.json())
            return None

        if resposta.ok:
            return resposta.json()

        # 500 e demais erros de servidor: espera progressiva e tenta de novo
        time.sleep(5 * (tentativa + 1))

    return None

Передовая практика

  • По возможности фильтруйте по муниципалитету или штату — без фильтрации запрос сканирует всю страну.
  • Предпочитайте инкрементальную синхронизацию id опуская всю базу каждый день.
  • Отправляйте запросы последовательно. API не рассчитан на десятки одновременных вызовов на одного клиента.
  • Перед повторением убедитесь, что ответ представляет собой список: сообщения об ограничении подкачки приходят со статусом 200.
  • Сохраните counting_id и block_id полученные — именно они позволяют вам сопоставлять записи Conta Ovos с записями в вашей системе.
  • Считайте эпидемиологическую неделю определяющим полем: Conta Ovos принимает только одно исследование на яйцеловушку в неделю и одно посещение на квартал в неделю.
  • Следуйте за страницей mudanças перед обновлением вашей интеграции.