REST API
발행
올리고, 읽고, 다시 시도하고, 지웁니다.
GET/v1/posts
최근 발행. 최신순이고 답글은 목록에 안 섞입니다.
페이지 넘기기
| 이름 | 타입 | 설명 |
|---|---|---|
limit | query | 1-100, defaults to 20. |
before | query | A publish id from a previous page's nextBefore. Returns the ones older than it. |
includeDeleted | query | true brings back posts you deleted from the channel. They are hidden by default. Our record and its metrics stay either way. |
status | query | Narrow to one or more statuses, comma separated: scheduled, draft, publishing, published, partial, failed or cancelled. Omit for everything, scheduled and draft included. |
since | query | YYYY-MM-DD or ISO 8601. Posts dated at or after this. A scheduled post is dated by scheduledAt, a sent one by when it was created. |
until | query | YYYY-MM-DD or ISO 8601. Posts dated at or before this; a date means the whole day. |
sort | query | scheduled orders by the post's date ascending and drops paging. Omit for newest first. |
hasMore | response | The list was cut short. |
nextBefore | response | Pass it back as before. Absent when there is nothing more. |
POST/v1/posts
연결된 계정 하나 이상에 올립니다.
| 이름 | 타입 | 설명 |
|---|---|---|
content필수 | string | Post text. Emoji count as UTF-8 bytes. |
accountIds필수 | string[] | Ids from GET /v1/accounts. |
mediaIds | string[] | Media already confirmed with complete. On Naver Blog you may leave out ids the body already points at with media:<id>; we read those from the text and attach them. |
options | object | Per-channel settings, keyed by the channel id. The shape is in GET /v1/platforms under options and on each channel page. Some channels cannot publish without it — YouTube needs options.youtube.title and Naver Blog needs options.naver_blog.title plus a category. Every channel takes options.<channel>.content, a different text for that channel alone. |
scheduledAt | string | ISO 8601 with a timezone offset, 10 minutes to a year out. We keep the post as scheduled and send it at that time, on every channel. |
draft | boolean | true saves the post without sending. It can have no accountIds yet. Send it later with POST /v1/posts/{id}/publish. |
batchId | string | Optional tag, up to 40 characters, to group posts made together (for example the same text sent to two workspaces). Comes back on every post. |
replyTo | string | Publish this as a reply to that post id. |
threadItems | object[] | Split a long post into a chain: [{ content, mediaIds? }]. The first item is the root, the rest become replies under it in order. Each item obeys the character limit on its own. Exclusive with content. Only Threads and Bluesky take a chain; the composer builds one with the item editor. If a later item fails the ones already up stay up, and the response carries thread.resumeFrom. Posts that have later items carry them back as threadItems, and PATCH takes the same field while the post is scheduled or a draft. |
topicTag | string | One topic to tag the post with. Threads takes exactly one and rejects periods and ampersands. On a thread it applies to the first piece only. |
Idempotency-Key | header | Send the same key when you retry and we return the first answer instead of publishing twice. Reusing a key with a different body is a 422. Kept for 24 hours. |
wait | boolean | Hold the response until the post is really out. Text waits up to 10 seconds, media up to 75. Past that you get the usual 202 with a next hint, and the post keeps going. Defaults to false. |
curl -X POST https://api.uplika.com/v1/posts \
-H "authorization: Bearer $UPLIKA_KEY" \
-H "content-type: application/json" \
-d '{
"content": "Now with images",
"accountIds": ["acc_01H..."],
"mediaIds": ["med_01H..."]
}'GET/v1/posts/{id}
발행 하나와 대상별 상태.
대상 상태
| 이름 | 타입 | 설명 |
|---|---|---|
publishing | status | In flight at the channel. |
published | status | Carries externalId and url. If the account was disconnected afterwards, the target stays published with connectionId null and accountHandle set to that account's handle until the same account is reconnected. |
partial | status | Some channels went out and some did not. Retry sends only the ones that failed. There is no 207 — publishing is 202 plus polling, so nothing is held open. |
cancelled | status | Deleted from the channel. Our record and its metrics stay. Hidden from GET /v1/posts unless you pass includeDeleted=true. |
failed | status | Carries error. |
scheduled | status | Held until scheduledAt. Nothing is on any channel yet. Editable. |
draft | status | Saved without a time. May have no targets. Editable. |
발행 하나에 채널마다 행이 하나입니다. publishing 은 진행 중, published 는 채널의 id 와 주소를 들고 있고, failed 는 사유를 들고 있습니다.
예약과 초안
scheduledAt 을 주면 글을 scheduled 로 잡고 있다가 그 시각에 우리 쪽에서 모든 채널로 보냅니다. draft: true 를 주면 누가 publish 를 부르거나 대시보드에서 띄우기 전까지 아무것도 안 나갑니다. 둘 다 status 가 scheduled 나 draft 로 오고, PATCH 로 고치고, /publish 로 앞당기고, DELETE 로 지웁니다. scheduledAt 은 시간대 오프셋이 있어야 하고 10분 뒤부터 1년까지입니다. 채널 자체 예약을 가진 채널도 있는데(options.youtube.publishAt, options.facebook.scheduledPublishTime, options.naver_blog.scheduledAt) API 로는 그대로 되지만 대시보드는 감춥니다. 한 글에 시계가 둘이면 어긋납니다.
채널마다 다른 본문
모든 채널이 options.<채널>.content 를 받습니다. 있으면 그 채널에만 공통 본문 대신 나가고, 길이·미디어 검사도 그 본문으로 합니다. 스레드 500자 글과 네이버 블로그 마크다운 본문이 한 호출에 들어가는 길입니다. 채널이 그 밖에 무엇을 받는지는 GET /v1/platforms 의 options 에 있습니다.
PATCH/v1/posts/{id}
예약·초안 글을 나가기 전에 고칩니다. 안 보낸 칸은 그대로고(네이버에만 가는 글은 mediaIds 를 빼면 새 본문의 media: 참조가 사진 목록입니다), options 는 통째로 바뀝니다. threadItems 는 체인을 통째로 바꾸고, content 만 보내면 첫 토막만 바뀝니다. 이미 나간 글의 수정은 비동기입니다. wait: true 를 주면 결과가 나올 때까지 응답을 붙잡고(최대 75초), 넘기면 202 와 함께 글을 다시 읽으라는 next 를 줍니다. 네이버 임시저장이 도중에 끊기면 네이버 임시저장함에서 찾아 있으면 회수합니다.
| 이름 | 타입 | 설명 |
|---|---|---|
content | string | New post text. |
accountIds | string[] | New target accounts. Replaces the current set. |
mediaIds | string[] | New media, in order. Replaces the current set. On a post going only to Naver Blog, leave it out when the body places media with media: references: those references become the list and media the new body does not place is removed (the response note says how many). |
options | object | Per-channel settings. Replaces the whole object. |
scheduledAt | string | New send time, same rules as on create. null turns the post into a draft. |
draft | boolean | true turns a scheduled post back into a draft. |
POST/v1/posts/{id}/publish
예약·초안 글을 지금 보냅니다. 예약 크론과 같은 함수라 두 번 나가지 않습니다.
| 이름 | 타입 | 설명 |
|---|---|---|
wait | boolean | Hold the response until the post is really out, same as on create. |
POST/v1/posts/{id}/retry
실패한 대상만 다시 시도합니다. 이미 나간 대상은 건너뜁니다. 네이버 임시저장 직후 끊긴 대상(naver_draft_unknown)이나 제때 확인되지 않은 네이버 답글(naver_reply_unknown)이 있으면 본문에 {"force": true} 를 실어야 다시 보냅니다. 같은 계정에 같은 글과 파일의 다른 글이 이미 있으면 그 글의 id 와 함께 409 duplicate_post 로 답하고 아무것도 다시 보내지 않습니다. {"force": true} 면 그래도 보냅니다.
DELETE/v1/posts/{id}
채널에서 글을 지우고 우리 기록도 삭제로 표시합니다.
예약·초안 글은 채널에 아무것도 없으므로 DELETE 가 우리 기록을 지웁니다. 나간 글은 채널에서 먼저 지우고 우리 기록은 지운 것으로 표시해 남깁니다.
채널에 이미 있는 것 읽기
우리 기록에는 우리를 거쳐 나간 것만 있습니다. 아래 넷은 채널 자체를 읽고, 어디에 올릴지를 고릅니다. list_channel_posts · get_quota · open_post · select_channels 뒤에 있는 라우트라 REST 로도 같은 답을 받습니다.
GET/v1/posts/{id}/thread
글 하나와 댓글·지표를 한 번에 받습니다.
GET/v1/channels/posts
앱에서 직접 쓴 글까지, 지금 채널에 있는 것.
쿼리
| 이름 | 타입 | 설명 |
|---|---|---|
accountId | query | Limit to one connected account. Omit to cover every channel. |
limit | query | 1-100, defaults to 25. |
GET/v1/channels/quota
24시간 한도를 채널이 얼마나 썼다고 말하는지.
GET/v1/channels/bridge
네이버 블로그 브리지(브라우저 확장)가 켜져 있는지, 꺼졌으면 어떻게 켜는지.
GET/v1/channels/publish-options
발행 전에 채널에 물어봐야 하는 것. TikTok 전용이다. 어느 닉네임으로 나가는지, 이 계정이 지금 고를 수 있는 공개 범위, 올릴 수 있는 상태인지, 영상 길이 상한을 준다. 다른 채널은 not_supported 다.
| 이름 | 타입 | 설명 |
|---|---|---|
accountId | query | Required. These values are per connected account and change when the person edits their channel settings, so do not cache them. |
POST/v1/accounts/select
연결된 채널 중 어디에 올릴지 고릅니다.
POST/v1/accounts/:id/refresh
채널 메타데이터(네이버 블로그 카테고리 등)를 다시 읽습니다. 브라우저 확장을 최대 1분 기다립니다.
| 이름 | 타입 | 설명 |
|---|---|---|
scope | string | string[] | Omit to list candidates. "all" for every active channel, or an array mixing platform names, handles and account ids. |
| 이름 | 타입 | 설명 |
|---|---|---|
blog필수 | string | A Naver blog id (2-40 letters, digits, - or _) or any link to the blog: blog.naver.com/<id>, m.blog.naver.com/<id>/<logNo>, PostView.naver?blogId=<id>. |
| 이름 | 타입 | 설명 |
|---|---|---|
keywords | string[] | Required. 1-300 keywords. Spaces are fine; the numbers come back per keyword as you sent it. |
save | boolean | Default false. true stores this research as a report (the keywords, the first set's main and subs, every set with its prompt) and returns reportId. The same person sending the same keyword set within ten minutes gets the existing reportId back with no new row and no judge call spent. |
seedText | string | What the person typed, kept with the saved report as its label. Defaults to the first five keywords. |
cacheOnly | boolean | Default false. true never calls Naver: keywords without a fresh cached value come back in unmeasured with no numbers. Use it after measuring in chunks, so the judge call is instant and spends nothing upstream. |
budgetMs | number | Time budget for new measurements, default and maximum 60000. When it runs out, no new upstream call starts; the response says partial: true and lists unmeasured. Call again and the cached part is skipped. |
| 이름 | 타입 | 설명 |
|---|---|---|
keywords | string[] | Required. 1-300 keywords. At most 40 of them are measured fresh in one call; the rest come back in unmeasured and are picked up by the next call. |
withDocs | boolean | Default true. false measures search volume only. |
budgetMs | number | Time budget for new measurements, default and maximum 60000. Past it the call stops starting upstream requests and returns partial: true with unmeasured. |
| 이름 | 타입 | 설명 |
|---|---|---|
seeds | string[] | Required. 1-10 seed keywords. Each is sent to Naver autocomplete once (one level); the words that come back are candidates to measure with judge, not proven keywords. Cached seven days across users. No daily quota: when the person's uplika Chrome extension answers there is no wait, and when our server answers, one call per person every 60 seconds (naver_autocomplete_busy with retryAfterSeconds). |
돌려주는 것
| 이름 | 타입 | 설명 |
|---|---|---|
found | { keyword, from }[] | Every word, the seeds first. from is seed, or L1:<seed> for a word that seed produced. |
errors | { query, error }[] | Seeds that could not be looked up, with why. |
cached | number | How many seeds came from the seven-day cache. |
via | { cache, extension, server } | How many seeds each path answered: the shared cache, the person's Chrome extension (in the background, from their own connection), or our server. |
| 이름 | 타입 | 설명 |
|---|---|---|
limit | number | Default 50, newest first. |
| 이름 | 타입 | 설명 |
|---|---|---|
keyword필수 | string | The keyword, as you would send it to measure. A query parameter, since keywords are usually Korean. |
limit | number | Default 100 days, newest first. |
GET/v1/research/youtube/search
검색어로 유튜브를 찾아 상위 영상의 조회수 · 구독자 · 조회/구독 비율 · 쇼츠/롱폼 · 길이 · 게시일과, 제목 · 훅을 뽑는 붙여넣을 프롬프트를 줍니다. 같은 검색은 24시간 캐시라 안 세고, 새 검색은 사람당 하루 횟수(응답의 quota)와 공용 YouTube Data API 단위를 씁니다. 기록에 reportId 로 저장됩니다.
| 이름 | 타입 | 설명 |
|---|---|---|
q필수 | string | The search words, as a viewer would type them. |
order | string | viewCount (default), date or relevance. |
period | string | 7d, 1m, 3m, 6m or 1y. Omit for no limit. |
format | string | all (default), shorts or long. 60 seconds or less counts as a Short. |
max | number | 1-50, default 25. |
topic | string | The video the person wants to make, one line. Goes into prompt and is saved with the report. |
GET/v1/research/youtube/reports
저장된 유튜브 검색 기록을 최신순으로 줍니다. 언제, 검색어와 필터, 주제, 영상 몇 편, 캐시였는지. 90일 보관합니다.
| 이름 | 타입 | 설명 |
|---|---|---|
limit | number | Default 50, newest first. |
GET/v1/research/youtube/reports/{id}
저장된 검색 하나를 그때의 영상 목록과 거기서 만든 프롬프트와 함께 줍니다.
| 이름 | 타입 | 설명 |
|---|---|---|
content | string | Required. The Markdown body, up to 60,000 characters. |
title | string | The post title. |
main | string | The main keyword. Without it the keyword checks cannot pass. |
subs | string[] | Sub keywords, one per section title. |
tags | string[] | The tags you will send in options.naver_blog.tags. |
fixedTags | string[] | Tags that need not appear in the body (brand tags). |
products | string[] | Product and brand names that must stay out of the first three paragraphs. |
mediaIds | string[] | The media ids attached to the post, to check that each is placed. |
| 이름 | 타입 | 설명 |
|---|---|---|
content필수 | string | The markdown body to lay out. |
mediaIds | string[] | Attached media, in order. Images the body does not reference are placed by the form. |
form | string | One preset id (default photo-story). Ignored when forms is given. |
forms | "all" | string[] | Lay the same body out in several presets at once; the response carries layouts[] so a person can compare and pick. |
| 이름 | 타입 | 설명 |
|---|---|---|
logNo필수 | string | The draft's id from GET /v1/naver/drafts. |
accountId | string | Required only when the workspace has more than one Naver Blog account. |
title / categoryId / category / tags / openType | string | Override the draft's own values. Left out, the draft goes out as it is. |
wait | boolean | Hold until the extension finishes, up to 75 seconds like a post with media on POST /v1/posts. Past that you get the usual 202 with a next hint, and the draft keeps going out. |
| 이름 | 타입 | 설명 |
|---|---|---|
topic | string | One section instead of the whole guide. The 400 lists the ids. |
| 이름 | 타입 | 설명 |
|---|---|---|
url | string | Required. A public Naver Blog post, like https://blog.naver.com/someone/224349824125. Only blog.naver.com links work, and private or deleted posts are refused. Five per minute. |
| 이름 | 타입 | 설명 |
|---|---|---|
url | string | Required. The same post link you passed to extract. Answers from our database only; Naver is not called. 404 reference_not_read when it was never read. |
publishing ──► published the channel accepted it, permalink is set
└─► partial some channels made it, some did not
└─► failed none of them made it, error says why
failed ──► publishing retry, and only the targets that failed go again
published ──► cancelled you deleted it; our record and its metrics stay자주 묻는 것
여러 채널에 한 번에 올리려면요?
accountIds 에 다 적어서 한 번만 부르면 됩니다. 요청 하나가 발행 하나를 만들고 채널마다 행이 하나씩 붙어서, 결과를 여러 호출에 걸쳐 맞춰 볼 필요가 없습니다.
202 는 무슨 뜻인가요?
글을 받았고 채널은 아직 대답하지 않았다는 뜻입니다. 무엇이 올라갔는지는 대상별 상태를 봐야 알고, 맨 위 응답은 일이 시작됐다는 것만 말합니다.
올라간 글을 지울 수 있나요?
됩니다. 채널에서 지우고 우리 기록은 삭제로 표시합니다. 그래서 그 글이 있었다는 것과 언제 나갔는지는 기록에 남습니다.