Scraper Hub API

flex 구성원·조직도

플레이그라운드에서 수집 →

채널 코드 flex_member · 수집 환경 windows

flex 구성원 목록(재직·휴직·입사/퇴사 예정)과 조직도(부서 트리)를 수집합니다. scope 로 범위를 고릅니다 — all(기본): items 는 구성원 1명 = 1건(이름·표시 이름·부서 경로·직책·직급·직무·재직 상태), 부서 트리는 완료 응답 최상위 channel_info.departments[] 로 1회 · members: 구성원만(부서 트리 없음) · departments: 조직도만(items 가 부서 1개 = 1건 — 경로·직속/하위 포함 인원·부서장). include_leave=false 면 휴직 중인 구성원을 빼고 부서 인원·부서장에서도 뺍니다. 이메일·휴대폰은 include_contact=true 일 때만. 수집 서버의 flex 수집기 앱이 전용 Chrome 프로필로 flex 에 로그인해 두고(세션 7일, 쓸 때마다 연장), 풀리면 앱에 저장된 아이디/비밀번호로 자동 로그인합니다 — 아이디/비밀번호는 API 로 보내지 않습니다. server 를 비우면 허브가 지금 가능한 flex 서버(온라인 · 로그인됨 · 대기 작업 적은 순)를 골라 배정하고, 가능한 서버가 없으면 409 no_flex_server 입니다. 지정한 서버가 오프라인이거나 flex 자동 로그인까지 실패한 상태면 409, flex 수집기가 없는 서버는 422 not_flex_server 입니다.

요청 파라미터

아래 값들을 POST /api/v1/collect 의 params 객체에 담아 보냅니다.

키타입필수기본설명
server text — — 비우면 허브가 지금 flex 수집이 가능한 서버(온라인 · flex 로그인됨 · 대기 작업 적은 순) 중 하나를 골라 배정하고, 응답의 server 로 알려줌. 특정 서버로 보내려면 그 서버 ID(예: win-flex-01, GET /flex-servers 로 조회)
scope enum — all all=구성원 items + channel_info.departments 부서 트리 · members=구성원 items 만 · departments=부서가 items(extra.kind=department)
include_leave bool — true false 면 휴직 중(재직 상태 LEAVE_OF_ABSENCE)인 구성원을 빼고 부서 인원·부서장 집계에서도 뺌. 휴직 예정자는 근무 중이라 항상 포함
include_contact bool — false 이메일·휴대폰 번호(extra.email / extra.phone) 포함 — 개인정보, 필요할 때만. 조직도만(departments)엔 해당 없음

요청 예시

curl -X POST https://scraper.conbus.co.kr/api/v1/collect \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "channel": "flex_member", "params": { "scope": "all", "include_leave": true, "include_contact": false } }'

응답 (202 Accepted)

요청은 즉시 큐에 적재되고 request_id 를 돌려줍니다. 실제 수집은 워커가 비동기로 처리합니다.

{ "success": true, "data": { "request_id": 42, "channel": "flex_member", "status": "pending", "status_url": "https://scraper.conbus.co.kr/api/v1/requests/42" } }

결과 조회 — GET /requests/{id}

같은 API 키로 request_id 를 조회합니다. 상태에 따라 응답이 달라집니다. 결과(items)는 요청별로 보관되므로 같은 글이 다른 요청(다른 키워드·재수집)에 겹쳐도 이 요청의 결과는 그대로입니다 — 여러 요청에 걸친 중복 정리는 post_id로 호출 쪽에서 하세요. 보존 기간은 완료 후 7일입니다. 이 채널은 '지금 상태'가 중요한 데이터라 짧게 보관합니다 — 결과는 받은 즉시 저장하세요. 보존 기간이 지나면 items 는 빈 배열이고 items_expired: true, items_keep_days 가 함께 옵니다(status 는 그대로 done).

진행 중 (pending / running)

아직 끝나지 않았으면 progress 로 진행 상황만 옵니다(items 없음).

{ "success": true, "data": { "request_id": 42, "channel": "flex_member", "status": "running", "external_ref": null, "result_count": null, "started_at": "2026-06-16T09:00:05+09:00", "finished_at": null, "duration_ms": null, "duration_sec": null, "created_at": "2026-06-16T09:00:00+09:00", "updated_at": "2026-06-16T09:01:30+09:00", "progress": { "count": 12, "phase": "list" }, "elapsed_ms": 85000 } }

완료 (done)

status 가 done 이면 data.items[] 에 결과가 담깁니다. 실패 시엔 status: "failed" 와 error 가 옵니다.

{ "success": true, "data": { "request_id": 42, "channel": "flex_member", "status": "done", "external_ref": null, "result_count": 1, "started_at": "2026-06-16T09:00:05+09:00", "finished_at": "2026-06-16T09:02:00+09:00", "duration_ms": 115000, "duration_sec": 115, "created_at": "2026-06-16T09:00:00+09:00", "updated_at": "2026-06-16T09:02:00+09:00", "channel_info": { "company": "주식회사 예시", "customer_id": "Ab12Cd34Ef", "member_count": 94, "departments": [ { "id": "YG0drZPZEw", "name": "회사", "parent_id": null, "parent_name": null, "path": "회사", "depth": 0, "display_order": 1, "visible": true, "member_count": 1, "total_count": 94, "heads": [ { "user_id": "Zx9Yw8Vu7T", "name": "김대표", "display_name": "대표", "job_title": "대표", "on_leave": false } ] }, { "id": "GBzM9bkdzJ", "name": "캠페인본부", "parent_id": "YG0drZPZEw", "parent_name": "회사", "path": "회사 > 캠페인본부", "depth": 1, "display_order": 2, "visible": true, "member_count": 0, "total_count": 54, "heads": [] }, { "id": "rjzQVQV2E7", "name": "마케팅팀", "parent_id": "GBzM9bkdzJ", "parent_name": "캠페인본부", "path": "회사 > 캠페인본부 > 마케팅팀", "depth": 2, "display_order": 3, "visible": true, "member_count": 5, "total_count": 5, "heads": [ { "user_id": "Qa2Ws3Ed4R", "name": "이팀장", "display_name": "팀장", "job_title": "팀장", "on_leave": false } ] } ] }, "items": [ { "post_id": "aB3dE5fG7h", "url": "https://flex.team/home?user_profile_uid=aB3dE5fG7h", "title": "길동", "author": "홍길동", "posted_at": null, "views": null, "comment_count": null, "recommends": null, "body": null, "source": "마케팅팀", "extra": { "kind": "member", "user_id": "aB3dE5fG7h", "name": "홍길동", "display_name": "길동", "english_name": null, "statuses": [ "IN_EMPLOY" ], "on_leave": false, "leave_scheduled": false, "primary_department": "마케팅팀", "primary_department_id": "rjzQVQV2E7", "department_path": "회사 > 캠페인본부 > 마케팅팀", "job_title": "팀원", "job_rank": "대리", "job_roles": [ "캠페인 AE" ], "job_groups": [], "job_levels": [], "is_head": false, "positions": [ { "department": "마케팅팀", "department_id": "rjzQVQV2E7", "department_path": "회사 > 캠페인본부 > 마케팅팀", "job_title": "팀원", "is_head": false, "is_primary": true } ], "about_me": null, "source_kind": "flex_member" } } ] } }

실패 (failed)

status 가 failed 면 error 에 사유가 옵니다(items 없음). 일시적 실패는 자동 재시도되며, 위 응답은 마지막 시도 기준입니다.

{ "success": true, "data": { "request_id": 42, "channel": "flex_member", "status": "failed", "external_ref": null, "result_count": 0, "started_at": "2026-06-16T09:00:05+09:00", "finished_at": "2026-06-16T09:00:16+09:00", "duration_ms": 11000, "duration_sec": 11, "created_at": "2026-06-16T09:00:00+09:00", "updated_at": "2026-06-16T09:00:16+09:00", "error": "flex 로그인 필요 — 자동 로그인 실패 — 아이디 또는 비밀번호가 틀렸습니다 (수집 PC 의 flex 수집기 계정 메뉴에서 확인 후 다시 요청)" } }

최상위 data에는 항상 request_id·channel·status·external_ref·result_count·started_at·finished_at·duration_ms·duration_sec·created_at·updated_at이 포함되고, 상태에 따라 items(done)·progress+elapsed_ms(진행 중)·error(failed/cancelled)가 추가됩니다. duration_*는 허브가 잰 수집 소요 시간(시작~종료) — 자세히는 비동기 & 콜백. 요청을 잘못 시작했다면 취소 API로 중단할 수 있습니다.

결과 필드 (items[])

필드설명
post_id구성원 사용자 ID(flex userIdHash, 동료 휴가의 extra.user_id 와 같은 값). scope=departments 면 부서 ID
urlflex 프로필 주소(로그인한 구성원만 열람). 부서 항목은 null
title / bodytitle=표시 이름(닉네임), 부서 항목은 부서명 / body=null
author실명. 부서 항목은 부서장 표시 이름(여럿이면 쉼표, 없으면 null)
posted_at작성 시각 (ISO8601, KST) — 구성원·부서는 null
views / comment_countnull / null
recommendsnull
source주 부서명. 부서 항목은 부서 경로
extra.kindmember · (scope=departments 이면) department
extra.name / display_name / english_name실명 · 표시 이름 · 영문 이름
extra.statuses / on_leave / leave_scheduled재직 상태 배열(IN_EMPLOY·LEAVE_OF_ABSENCE·LEAVE_OF_ABSENCE_SCHEDULED·RESIGNATION_SCHEDULED·IN_APPRENTICESHIP) · 휴직 중(LEAVE_OF_ABSENCE) · 휴직 예정
extra.primary_department / primary_department_id / department_path주 부서 · 부서 ID · 최상위부터의 부서 경로( > 구분)
extra.job_title / job_rank / job_roles / job_groups / job_levels직책 · 직급 · 직무 · 직군 · 레벨
extra.is_head부서장(리더) 여부
extra.positions겸직 포함 소속 목록
extra.about_me자기소개
extra.email / phoneinclude_contact=true일 때만
extra.(부서 항목) department_id / name / parent_id / parent_name / path / depth / display_order / visible부서 ID · 이름 · 상위 부서 · 경로 · 깊이(최상위 0) · 조직도 표시 순서 · 조직도 표시 여부
extra.(부서 항목) member_count / total_count / heads직속 인원(겸직 포함) · 하위 부서 포함 고유 인원 · 부서장 [{user_id, name, display_name, job_title, on_leave}] — include_leave=false 면 휴직자 빼고 셈
extra.source_kindflex_member
채널 참고
  • 수집 서버 자동 선택 — server 는 선택입니다. 비우면 허브가 지금 flex 수집이 가능한 서버(온라인 · flex 로그인됨 · 대기 작업 적은 순) 중 하나를 골라 배정하고 202 응답의 server 로 알려줍니다. 가능한 서버가 없으면 409 no_flex_server(이유 포함). 특정 서버로 보내려면 GET /flex-servers 의 서버 이름을 넣으세요 — 그 서버가 오프라인이면 409 server_offline, flex 로그인이 풀리고 자동 로그인까지 실패한 상태면 409 account_login_required(추가 인증 화면이면 account_blocked), flex 수집기가 없는 서버면 422 not_flex_server. 배정은 요청 시점에 고정되므로 고른 서버가 그 뒤 꺼지면 그 요청은 실패로 끝납니다(다시 요청하면 살아 있는 서버로 갑니다).
  • 범위(scope) — all(기본): items = 구성원, 부서 트리는 완료 응답 최상위 channel_info.departments[] 로 1회 · members: 구성원만(channel_info 에 회사 정보만) · departments: 부서가 items(부서 1개 = 1건, extra.kind=department, 조직도 순서 — 상위 → 하위, 형제는 flex 표시 순서).
  • 휴직자(include_leave) — 기본 포함. false 면 재직 상태가 휴직(LEAVE_OF_ABSENCE)인 구성원을 빼고, 부서 인원·부서장 집계에서도 뺍니다. 판정은 flex 재직 상태값이며 조직도의 "휴직" 같은 부서 이름과는 무관합니다. 휴직 예정자는 아직 근무 중이라 항상 포함됩니다. 뺀 인원 수는 관리자 화면의 summary.on_leave_excluded.
  • channel_info 의 부서 트리 항목은 {id, name, parent_id, parent_name, path, depth, display_order, visible, member_count, total_count, heads} — parent_id 로 트리를 만들고, visible=false 는 flex 조직도에서 숨긴 부서입니다. 인원은 가져온 구성원으로 센 값이라 flex 조직도 화면의 숫자와 같습니다(휴직자 제외 시 그만큼 적음).
  • 퇴사자는 포함하지 않습니다(flex 구성원 화면의 "전체" 와 같은 범위). 이메일·휴대폰은 개인정보라 기본으로 빼고, include_contact=true 일 때만 담습니다.

결과 수신(폴링·콜백) 방식은 비동기 & 콜백을 참고하세요.