Jak wysłać wiele ofert do Vereel z arkusza przez API

Jeśli Twoje kampanie już leżą w arkuszu albo pliku z produktami, nie musisz ich przepisywać. Krótki skrypt przeczyta wiersze i wyśle każdy do Vereel jako ofertę. Limity API wyznaczają, jak zrobić to dobrze: Vereel weryfikuje każdą ofertę, więc partia to kolejka, a nie lawina. Oto skrypt, który pracuje z tymi limitami, zamiast z nimi walczyć.
Co limity oznaczają dla partii
Każda oferta wysłana przez API czeka na weryfikację przez osobę z Vereel. Żeby dało się to ogarnąć, marka może mieć naraz do 20 ofert w weryfikacji i wysłać do 50 nowych ofert na dobę. Jeden token może wykonać 30 zmian w ciągu 10 minut. Dwudziesta pierwsza oferta z rzędu jest odrzucana z kodem REVIEW_QUEUE_FULL, dopóki część z pierwszych 20 nie zostanie sprawdzona.
Planuj więc partie po najwyżej 20 ofert i wysyłaj następną, gdy pierwsze zostaną zatwierdzone albo zwrócone. Sto ofert to pięć partii rozłożonych na kilka dni, a nie jedno uruchomienie.
Przygotuj arkusz
Jeden wiersz to jedna oferta. Zapisz arkusz jako plik CSV o nazwie offers.csv (UTF-8) z kolumnami:
- name i description: tekst, który widzą twórcy.
- platforms: instagram, tiktok, youtube albo x, rozdzielone średnikami.
- category: nazwa kategorii Vereel, dokładnie taka, jak podaje ją GET /categories.
- product_value i included: co dostaje twórca i ile to jest warte w zł.
- image_url: link do JPEG, PNG lub WebP do 5 MB pod publicznym adresem https.
Skrypt
Przykład wysyła oferty barterowe na produkt. Płatna współpraca dodaje do tego samego zapytania isPayment i additionalPayment.
Zapisz skrypt jako send_offers.py, wpisz token do zmiennej środowiskowej VEREEL_TOKEN (nigdy do pliku) i uruchom go w Pythonie 3 z biblioteką requests. Skrypt wysyła wiersze po kolei i zatrzymuje się wtedy, kiedy trzeba.
import csv
import os
import time
import requests
API = "https://www.vereel.com/api/v1/public"
HEADERS = {"Authorization": f"Bearer {os.environ['VEREEL_TOKEN']}"}
# Vereel's category names, mapped to the ids the API wants.
categories = requests.get(f"{API}/categories", timeout=30).json()["data"]
category_id = {c["name"]: c["id"] for c in categories}
def offer_from(row):
return {
"name": row["name"],
"description": row["description"],
"offerType": "PRODUCT",
"platforms": row["platforms"].split(";"),
"categoryIds": [category_id[row["category"]]],
"isPayment": False,
"isBarter": True,
"productServiceValue": float(row["product_value"]),
"currency": "PLN",
"includedDescription": row["included"],
"imageUrls": [row["image_url"]],
}
def send(row):
"""Returns "sent", "skipped" or "stop"."""
for attempt in range(3):
try:
res = requests.post(f"{API}/my/offers", json=offer_from(row), headers=HEADERS, timeout=90)
except requests.RequestException as err:
# The offer may have been created anyway: look before sending it again.
print("no answer for", row["name"], err, "- check GET /my/offers first")
return "stop"
if res.status_code == 201:
print("sent", row["name"], res.json()["data"]["id"])
return "sent"
try:
error = res.json()["error"]
except ValueError:
print("unexpected answer", res.status_code, "for", row["name"])
return "stop"
if error["code"] == "REVIEW_QUEUE_FULL":
return "stop"
if error["code"] == "RATE_LIMITED":
time.sleep(int(res.headers.get("Retry-After", "60")))
continue
print("skipped", row["name"], error["code"], error["details"])
return "skipped"
return "stop"
with open("offers.csv", newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
if send(row) == "stop":
print("Stopped at", row["name"], "- run again later with the rows that are left.")
breakCo skrypt robi, gdy coś pójdzie nie tak
Obsługuje cztery przypadki, a każdy to wybór, który możesz zmienić:
- Wiersz odrzucony przez API (422): wypisuje błąd z nazwą każdego błędnego pola, pomija wiersz i idzie dalej.
- Za dużo zapytań (RATE_LIMITED): czeka tyle, ile mówi nagłówek Retry-After, i ponawia ten sam wiersz, do trzech razy.
- Pełna kolejka weryfikacji (REVIEW_QUEUE_FULL): zatrzymuje się. Kilka minut czekania nie pomoże; kolejka opróżnia się w miarę weryfikacji. Uruchom go ponownie później z pozostałymi wierszami.
- Brak odpowiedzi (przekroczony czas albo zerwane połączenie): zatrzymuje się, bo oferta mogła zostać utworzona. API nie ma klucza idempotentności, więc ponowne wysłanie wiersza mogłoby utworzyć duplikat. Najpierw zajrzyj do GET /my/offers.
Sprawdź, co się stało
Każda wysłana oferta powstaje ze statusem in_review. GET /my/offers wyświetla je z ich statusem: active po zatwierdzeniu albo rejected z uwagą recenzenta, gdy Vereel zwróciło ofertę do Twoich wersji roboczych. Odrzucona oferta jest wersją roboczą w Twoich kampaniach: możesz ją poprawić i opublikować w aplikacji albo wysłać poprawioną.
Zapisuj identyfikator, który skrypt wypisuje przy każdym wierszu. To najprostszy ślad łączący wiersz arkusza z ofertą w Vereel.
Nawyki, które oszczędzają czas
Kilka drobiazgów oszczędza godziny:
- Najpierw wyślij trzy wiersze, potem cały arkusz.
- Trzymaj zdjęcia małe i dostępne: Vereel pobiera każde z nich w trakcie tworzenia oferty, a obraz, którego nie może pobrać, powoduje odrzucenie całej oferty.
- Użyj osobnego tokena dla skryptu i nazwij go od skryptu, żeby unieważnić go po zakończeniu zadania.
Arkusz potrafi szybko zasilić Vereel, ale weryfikacja trzyma tempo na ludzką miarę. Wysyłaj partiami, zapisuj identyfikatory i pozwól skryptowi zatrzymać się, zamiast zgadywać.
Najczęstsze pytania
- Ile ofert mogę wysłać naraz?
- Do 20 może czekać na weryfikację jednocześnie, a nowych ofert z API możesz wysłać do 50 na dobę. Dwudziesta pierwsza oferta z rzędu jest odrzucana, dopóki część nie zostanie sprawdzona.
- Czy skrypt może opublikować oferty?
- Nie. Oferty wysłane przez API czekają na weryfikację Vereel. Publiczne stają się dopiero po zatwierdzeniu.
- Co jeśli skrypt zatrzyma się w połowie?
- Zajrzyj do GET /my/offers, żeby zobaczyć, które wiersze utworzono, usuń je z arkusza i uruchom skrypt ponownie. API nie chroni przed duplikatami, więc nie wysyłaj ponownie wiersza, którego nie sprawdzono.
- Czy muszę znać Pythona?
- Nie. Skrypt pokazuje tylko kroki: przeczytaj wiersz, wyślij jedno zapytanie, sprawdź odpowiedź. To samo zrobi każdy język, który potrafi wysłać zapytanie HTTP i przeczytać plik CSV.