같은 게시물을 세 번 올리고 나서야 이유를 알았다
자동화 봇을 테스트하면서 limit이 자꾸 걸렸다. AI한테 물어봤더니 이런 말이 돌아왔다.
"앱이 개발 중 상태라 제약이 있어요. 24-48시간 기다리면 풀릴 거예요."
그럴싸했다. 믿었다.
Meta 콘솔 토끼굴
기다렸다. 또 안 됐다.
그럼 앱 게시 문제인가 싶었다. Meta 개발자 콘솔 들어갔다. AI가 알려준 버튼이 없었다. 메뉴가 달랐다. 다른 경로로 들어갔다. 권한 항목 찾아서 추가했다. 앱 검수 신청 화면 들어갔다. 뭔가 테스트를 완료해야 한다고 해서 실행해봤다. 다시 권한 뺐다가 넣었다. 토큰 재발급 받았다. 또 테스트.
인증 화면까지 들어갔더니 사업자 등록 정보를 넣으라고 했다. 거기서 멈췄다. 이건 아니다 싶었다.
Graph API Explorer 열어서 직접 뒤적이기 시작했다. 뭔가 나오긴 했는데 뭘 보고 있는 건지도 불분명했다.
"중복 자료가 왜 이렇게 많냐"
그러다 인스타그램을 직접 열어봤다. 같은 게시물이 세 개였다. 피드 전체 보니까 12개. 여러 기사에서 중복이 각각 쌓여있었다.
코드 흐름을 다시 따라갔다. 403 받으면 실패로 처리하고, DB 업데이트 안 하고, 다음 스케줄에 재시도. 그 재시도도 403. 또 재시도. 근데 인스타에는 매번 올라갔다. 몇 사이클 돌고 나니 이렇게 됐다.
받은 에러:
{
"error": {
"message": "Application request limit reached",
"type": "OAuthException",
"code": 4,
"error_subcode": 2207051,
"is_transient": false
}
}
rate limit이겠지 했다. 확인해봤다:
r = await client.get(
f"{GRAPH_URL}/{user_id}/content_publishing_limit",
params={"fields": "config,quota_usage", "access_token": token}
)
quota_usage: 0. 한도엔 전혀 안 걸려있었다. x-app-usage 헤더도 0%. rate limit이 아니었다.
행동 차단인가. 근데 행동 차단이 됐으면 어딘가 뜰 거 아냐? 앱, Business Suite, 개발자 콘솔. 아무것도 없었다. 경고도, 배너도, 표시 하나도.
행동 차단은 어디에도 안 뜬다. 에러 응답의 is_transient: false가 유일한 신호다. rate limit은 보통 is_transient: true에 24시간 후 풀린다. false면 기다린다고 해결 안 된다. 이게 구분 기준이었다.
원인은 테스트하면서 1시간 안에 게시물 13개를 올린 거였다. 일일 한도 25개엔 한참 못 미치는데. 단시간에 몰아 올리면 자동화 패턴으로 감지돼서 별도 제한이 걸린다. 문서엔 없다.
이 차단 상태에서 media_publish는 403을 뱉는데 게시물은 올라간다. 코드가 403을 실패로만 처리했으니 재시도했고, 재시도마다 중복이 쌓였다.
이렇게 고쳤다:
async def _publish_container(client, container_id):
resp = await client.post(
f"{GRAPH_URL}/{IG_USER_ID}/media_publish",
params={"creation_id": container_id, "access_token": IG_TOKEN},
)
if resp.is_success:
return resp.json().get("id")
if resp.status_code == 403:
await asyncio.sleep(3)
check = await client.get(
f"{GRAPH_URL}/{IG_USER_ID}/media",
params={"fields": "id,timestamp", "limit": 1, "access_token": IG_TOKEN},
)
if check.is_success:
data = check.json().get("data", [])
if data:
ts = datetime.fromisoformat(data[0]["timestamp"].replace("Z", "+00:00"))
if datetime.now(timezone.utc) - ts < timedelta(seconds=60):
return data[0]["id"]
resp.raise_for_status()
403 받으면 바로 최근 미디어 조회. 60초 이내에 올라온 게 있으면 성공으로 친다.
"추가했는데 해볼래?" 그리고 또 실패
중복 12개 지워야 했다. 삭제엔 instagram_manage_contents 권한이 필요한데 내 토큰엔 없었다.
권한 추가. 확인. 삭제 시도.
{"error": {"message": "(#10) Insufficient permissions", "code": 10}}
실패. 다시. 같은 에러. 콘솔엔 분명히 있는데.
OAuth 토큰은 발급 시점의 scope를 고정으로 가진다. 앱에 권한 추가해도 기존 토큰은 그대로다. 새로 발급받아야 한다.
토큰이 실제로 뭘 갖고 있는지 보려면:
r = await client.get(
"https://graph.facebook.com/v22.0/me/permissions",
params={"access_token": token}
)
granted = [p["permission"] for p in r.json()["data"] if p["status"] == "granted"]
instagram_manage_contents가 없으면 Graph API Explorer에서 scope 체크하고 재발급. 그 다음엔 됐다.
진짜 원인 조합
| 신호 | 의미 |
|---|---|
quota_usage: 0인데 403 |
행동 차단 (rate limit 아님) |
is_transient: false |
기다려도 안 풀림 |
| 403인데 인스타에 게시됨 | 차단 상태에서도 게시는 통과함 |
| 행동 차단이 UI에 표시됨 | 안 됨, 어디에도 |
API 동작은 문서에 없고, AI는 자신 있게 틀린 경로를 알려줬고, 그 경로의 콘솔 UI는 또 바뀌어있었다. 어디서부터 잘못됐는지 찾는 게 제일 어려웠다.
AI가 알려주는 콘솔 메뉴는 실제랑 다를 수 있다. API가 이상하다 싶으면 UI 말고 응답 필드 먼저 봐라. is_transient 하나가 대시보드보다 정직했다.
오후 하나 날렸다. 중복 12개, Meta 토끼굴, 권한 삽질. 그나마 건진 건 이 글이다.
Instagram Graph API v22.0. 카드뉴스 자동 발행 봇 만들면서 겪은 내용. 봇은 @dogfootbro.ai에 올라간다.
Top comments (0)