개발자 API 문서
아이비봇과 이 사이트를 잇는 API 예요. 직접 봇을 만드시거나, 아이비봇의 상태를 다른 곳에서 쓰고 싶으실 때 보세요.
주소
동작은 주소 뒤
주고받는 것은 전부 JSON (UTF-8) 이고, 응답은 언제나
https://ibcytb.com/bot/api.php동작은 주소 뒤
?a= 로 고릅니다. 예) https://ibcytb.com/bot/api.php?a=ping주고받는 것은 전부 JSON (UTF-8) 이고, 응답은 언제나
{"ok": true} 또는 {"ok": false} 로 시작해요.
1. 인증
헤더 두 개를 붙이세요.
| 헤더 | 값 | 필수 |
|---|---|---|
X-Ivy-Key |
ivy_ab12cd.xxxxxxxxxxxx | 예 |
X-Ivy-Ts |
지금 시각 (초 단위 유닉스 시간) | 권장 |
Content-Type |
application/json; charset=UTF-8 | POST 일 때 |
권한
| 권한 | 쓸 수 있는 동작 |
|---|---|
bot | heartbeat · plans · room |
bot,admin | 위의 전부 + setup · rooms |
X-Ivy-Ts 가 왜 필요한가요
지나간 요청을 그대로 다시 보내는 것을 막습니다. 서버 시각과 5분 넘게 차이나면
지나간 요청을 그대로 다시 보내는 것을 막습니다. 서버 시각과 5분 넘게 차이나면
clock_skew 로 거절해요.
폰 시계를 자동으로 맞춰 두세요. 헤더를 아예 안 보내면 이 검사를 건너뜁니다.
2. 호출 제한
- 키 하나당 1분에 240회까지. 넘기면
429 too_many예요. - 1분 신호 + 여유를 잡은 값이라 정상적으로 쓰시면 걸릴 일이 없어요.
- 모든 호출은 관리자 → 봇 연동 의 호출 기록에 남습니다.
3. 동작
| 동작 | 방식 | 권한 | 하는 일 |
|---|---|---|---|
ping |
GET | 없어도 됨 |
서버가 살아 있는지 봅니다. 키 없이 부를 수 있는 유일한 동작이에요. |
heartbeat |
POST | bot |
봇이 살아 있다고 알립니다. 1분마다 보내세요. 가동률이 이 신호로 계산돼요. |
plans |
GET | bot |
플랜, 기능 허용표, 제한값, 방별 플랜을 한 번에 받습니다. heartbeat 의 rev 가 바뀌었을 때만 다시 부르세요. |
room |
GET | bot |
방 하나의 플랜과 열린 기능을 봅니다. 주소에 &key=방식별값 을 붙이세요. |
setup |
POST | admin |
방을 등록하거나 플랜을 바꿉니다. $setup 이 쓰는 곳이에요. 방이 없으면 새로 만듭니다. |
rooms |
GET | admin |
등록된 방을 전부 봅니다. |
GET ?a=ping
없어도 됨
서버가 살아 있는지 봅니다. 키 없이 부를 수 있는 유일한 동작이에요.
돌아오는 것
{"ok":true,"pong":true,"time":"2026-09-27 01:00:00","rev":"6114d4b107de"}
POST ?a=heartbeat
bot
봇이 살아 있다고 알립니다. 1분마다 보내세요. 가동률이 이 신호로 계산돼요.
보내는 것
{
"device": "phone-01",
"label": "아이비봇 폰",
"version": "1.7.0",
"uptime": 9000,
"rooms": ["1234567890", "9876543210"],
"cpu": "12.4%",
"mem": "2.1GB / 5.8GB (36%)",
"ping": 38
}
돌아오는 것
{
"ok": true,
"server_time": "2026-09-27 01:00:00",
"rev": "6114d4b107de",
"online": true,
"uptime_24h": 99.8
}
rooms 에 넣은 방들은 "마지막 신호" 시각이 갱신됩니다.
등록되지 않은 방 식별값은 조용히 무시해요.
GET ?a=plans
bot
플랜, 기능 허용표, 제한값, 방별 플랜을 한 번에 받습니다. heartbeat 의 rev 가 바뀌었을 때만 다시 부르세요.
돌아오는 것
{
"ok": true,
"rev": "6114d4b107de",
"plans": [
{"code":"free","name":"무료","price":"0원","price_won":0,
"final_won":0,"discount":null,"summary":"..."},
{"code":"pro","name":"프로","price":"20,000원","price_won":20000,
"final_won":16000,
"discount":{"type":"percent","value":20,"label":"가을 할인","until":"2026-10-31 23:59:59"},
"summary":"..."}
],
"features": {"translate":{"name":"번역","group":"lookup","always":false}, "...": {}},
"matrix": {"free":["calc","weather"], "pro":["..."]},
"limits": {"free":{"notes_max":20}, "pro":{"notes_max":-1}},
"limit_meta": {"notes_max":{"name":"노트 저장 개수","unit":"개","higher":true}},
"always": ["system","admin_tools"],
"rooms": {"1234567890":{"plan":"pro","active":true,"name":"컨볼 소통방"}},
"default_plan": "free"
}
GET ?a=room
bot
방 하나의 플랜과 열린 기능을 봅니다. 주소에 &key=방식별값 을 붙이세요.
돌아오는 것
{
"ok": true,
"rev": "6114d4b107de",
"registered": true,
"room": {"key":"1234567890","name":"컨볼 소통방","plan":"pro",
"manager_link":true,"active":true},
"plan": "pro",
"features": ["translate","points","games","..."],
"limits": {"notes_max":-1,"gift_daily":10000}
}
POST ?a=setup
admin
방을 등록하거나 플랜을 바꿉니다. $setup 이 쓰는 곳이에요. 방이 없으면 새로 만듭니다.
보내는 것
{
"room_key": "1234567890",
"room_name": "컨볼 소통방",
"plan": "standard",
"manager_link": true,
"by": "아이비"
}
돌아오는 것
{
"ok": true,
"created": false,
"rev": "8d62bb0012d4",
"room": {"key":"1234567890","name":"컨볼 소통방","plan":"standard","manager_link":true},
"features": ["translate","points","..."],
"limits": {"notes_max":200,"gift_daily":3000}
}
plan 을 빼면 플랜은 그대로 두고 이름이나 연동 여부만 바꿉니다.
없는 플랜 코드를 보내면 400 이에요.
GET ?a=rooms
admin
등록된 방을 전부 봅니다.
돌아오는 것
{
"ok": true, "rev": "...", "count": 3,
"rooms": [
{"key":"1234567890","name":"컨볼 소통방","plan":"pro",
"manager_link":true,"active":true,"last_seen":"2026-09-27 00:59:12"}
]
}
4. rev 로 바뀐 것만 받기
모든 응답에는 rev 가 들어 있어요.
플랜, 기능 허용표, 제한값, 할인, 방 목록 중 무엇이든 바뀌면 이 값이 달라집니다.
봇은 이렇게 쓰면 됩니다.
- 1분마다
heartbeat를 보낸다 - 응답의
rev가 갖고 있던 값과 같으면 아무것도 하지 않는다 - 다르면
plans를 한 번 불러 캐시를 갈아 끼운다
5. 기능 키 목록
matrix 와 features 에 나오는 키예요.
| 키 | 이름 | 분류 | 항상 열림 |
|---|---|---|---|
system |
봇 기본 명령어 | 기본 (항상 열림) | 예 |
admin_tools |
관리자 도구 | 기본 (항상 열림) | 예 |
welcome |
입장 시 안내 멘트 | 플랜 기능 (이용약관 표) | 아니오 |
levelup |
레벨업 | 플랜 기능 (이용약관 표) | 아니오 |
weather |
날씨 | 플랜 기능 (이용약관 표) | 아니오 |
attendance |
출석체크 및 순위 경쟁 | 플랜 기능 (이용약관 표) | 아니오 |
chatlog |
채팅기록 | 플랜 기능 (이용약관 표) | 아니오 |
fever |
채팅랭킹 피버타임 | 플랜 기능 (이용약관 표) | 아니오 |
custom_cmd |
사용자 지정 명령어 | 플랜 기능 (이용약관 표) | 아니오 |
translate |
번역 | 조회·번역 | 아니오 |
dict |
사전 | 조회·번역 | 아니오 |
spell |
맞춤법 검사 | 조회·번역 | 아니오 |
fx |
환율·코인 | 조회·번역 | 아니오 |
calc |
계산기 | 조회·번역 | 아니오 |
note |
노트 | 조회·번역 | 아니오 |
air |
미세먼지 | 생활 정보 | 아니오 |
time |
시간·일출·일몰 | 생활 정보 | 아니오 |
transit |
교통 | 생활 정보 | 아니오 |
sky |
하늘 | 생활 정보 | 아니오 |
earth |
지구 | 생활 정보 | 아니오 |
textkit |
글자 도구 | 생활 정보 | 아니오 |
netspeed |
인터넷 속도 측정 | 생활 정보 | 아니오 |
points |
포인트·상점·업적·칭호 | 재미·게임 | 아니오 |
games |
게임 | 재미·게임 | 아니오 |
events |
이벤트 | 재미·게임 | 아니오 |
mimic |
따라쟁이·너찾는자 | 재미·게임 | 아니오 |
antispam |
도배 감지 | 관리·감지 | 아니오 |
badword |
욕설 감지 | 관리·감지 | 아니오 |
nickwatch |
닉네임 변경 기록 | 관리·감지 | 아니오 |
warn |
경고 부여 | 관리·감지 | 아니오 |
6. 제한 키 목록
limits 에 나오는 키예요. 값은 정수이고,
-1 은 무제한, 0 은 항목마다 뜻이 다릅니다.
| 키 | 이름 | 단위 | 0 의 뜻 | 큰 값이 좋은가 |
|---|---|---|---|---|
rooms_max |
등록 가능한 방 | 개 | 없음 | 예 |
notes_max |
노트 저장 개수 | 개 | 쓸 수 없음 | 예 |
chatlog_days |
채팅기록 보관 기간 | 일 | 저장 안 함 | 예 |
rank_top |
랭킹 표시 인원 | 명 | 표시 안 함 | 예 |
gift_once |
한 번에 선물 한도 | P | 보낼 수 없음 | 예 |
gift_daily |
하루 선물 한도 | P | 보낼 수 없음 | 예 |
point_daily |
하루 포인트 적립 상한 | P | 적립 안 함 | 예 |
cmd_cooldown |
명령어 쿨다운 | 초 | 없음 | 아니오 (작을수록 좋음) |
custom_cmd_max |
사용자 지정 명령어 | 개 | 만들 수 없음 | 예 |
quiz_daily |
하루 퀴즈 횟수 | 회 | 쓸 수 없음 | 예 |
7. 오류
실패하면 이렇게 돌아옵니다. HTML 이 튀어나오는 일은 없어요.
{"ok":false,"error":"bad_key","message":"키가 맞지 않습니다."}
| 상태 | error | 뜻 |
|---|---|---|
| 401 | no_key |
X-Ivy-Key 헤더가 없습니다. |
| 401 | bad_key |
키가 맞지 않거나 꺼진 키입니다. |
| 403 | bad_request |
이 키의 권한으로는 할 수 없는 동작입니다. |
| 400 | clock_skew |
기기 시계가 서버와 5분 넘게 차이납니다. |
| 400 | bad_request |
보낸 값이 모자라거나 잘못됐습니다. |
| 404 | unknown_action |
그런 동작이 없습니다. |
| 429 | too_many |
1분에 240회를 넘겼습니다. |
| 503 | not_upgraded |
사이트 테이블이 아직 준비되지 않았습니다. |
| 503 | db |
데이터베이스에 연결하지 못했습니다. |
| 500 | server |
서버에서 문제가 생겼습니다. |
8. 붙여 쓰는 예시
curl
curl -s "https://ibcytb.com/bot/api.php?a=ping"
curl -s -X POST "https://ibcytb.com/bot/api.php?a=heartbeat" \
-H "X-Ivy-Key: ivy_ab12cd.xxxxxxxxxxxx" \
-H "X-Ivy-Ts: $(date +%s)" \
-H "Content-Type: application/json" \
-d '{"device":"phone-01","version":"1.7.0","uptime":9000}'
메신저봇R (Rhino ES5)
function callIvy(action, body) {
var conn = new java.net.URL("https://ibcytb.com/bot/api.php?a=" + action).openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("X-Ivy-Key", API_KEY);
conn.setRequestProperty("X-Ivy-Ts", String(Math.floor(new Date().getTime() / 1000)));
if (body) {
var payload = new java.lang.String(JSON.stringify(body)).getBytes("UTF-8");
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
var os = conn.getOutputStream();
os.write(payload); os.flush(); os.close();
}
var code = conn.getResponseCode();
var br = new java.io.BufferedReader(new java.io.InputStreamReader(
code >= 400 ? conn.getErrorStream() : conn.getInputStream(), "UTF-8"));
var sb = new java.lang.StringBuilder(), line;
while ((line = br.readLine()) !== null) { sb.append(line); }
br.close();
conn.disconnect();
return JSON.parse(String(sb.toString()));
}
파이썬
import json, time, urllib.request
API = "https://ibcytb.com/bot/api.php"
KEY = "ivy_ab12cd.xxxxxxxxxxxx"
def call(action, body=None, **query):
url = API + "?a=" + action
for k, v in query.items():
url += "&" + k + "=" + urllib.parse.quote(str(v))
data = json.dumps(body, ensure_ascii=False).encode() if body else None
req = urllib.request.Request(url, data=data, method="POST" if data else "GET")
req.add_header("X-Ivy-Key", KEY)
req.add_header("X-Ivy-Ts", str(int(time.time())))
if data:
req.add_header("Content-Type", "application/json")
with urllib.request.urlopen(req, timeout=20) as r:
return json.loads(r.read().decode())
print(call("ping"))
print(call("room", key="1234567890")["plan"])도움이 필요하시면
막히는 곳이 있으면 부방장 아이비의 1:1 채팅으로 알려주세요. 키는 절대 다른 사람에게 보내지 마세요. 키가 있으면 플랜을 바꿀 수 있어요.