HiAPI
블로그

튜토리얼

HiAPI API로 grok-4.7을 사용하는 방법: curl, Python과 실제로 동작하는 요청

첫 요청부터 응답 검증까지의 실전 가이드

Nov 3, 2026
HiAPI 博客教程封面图:多层半透明玻璃平面在浅色背景上折射出克制的冷蓝色带。

첫 요청 전에 준비해야 할 것

API로 무언가를 보내기 전에 네 가지가 준비되어 있는지 확인하세요. 이 중 하나라도 빠뜨리는 것이 첫 호출 실패의 가장 흔한 원인이며, 그 실패 원인은 대개 모델 자체와는 전혀 무관합니다.

HiAPI 계정

유효한 HiAPI.ai 계정이 필요합니다. 대시보드에 로그인하여 프로젝트, 키, 사용량을 관리하세요. 이 튜토리얼의 모든 작업은 동일한 통합 API 인터페이스를 통해 이루어지므로, 여기서 만든 키는 플랫폼이 제공하는 여러 모델 간에 공통으로 사용할 수 있습니다.

API 키

대시보드에서 API 키를 만들고 안전한 곳에 보관하세요. 테스트할 때는 셸 환경 변수를, 실제 운영에 투입할 코드에는 키 관리자를 사용하세요. 키를 코드 저장소에 커밋하거나 클라이언트 앱에 붙여넣지 마세요.

로컬 테스트용이라면 셸 세션마다 한 번씩 내보내면 됩니다:

export HIAPI_API_KEY="your-api-key-here"

기본 URL

기본 URL은 HiAPI API의 루트 주소입니다. 이 튜토리얼의 모든 요청은 이 주소를 기준으로 합니다. 대시보드에서 계정에 사용해야 할 정확한 기본 URL을 확인하고, 스크립트와 앱 코드가 같은 값을 참조하도록 변수에 저장하세요:

export HIAPI_BASE_URL="https://api.hiapi.ai"

모델 식별자

이 튜토리얼에서 사용하는 모델 식별자는 grok-4.7입니다. 이 값은 순수 문자열로 요청 본문에 전달됩니다. 식별자가 잘못되면 보통 조용히 대체되는 대신 명확한 오류가 반환되므로 오타를 쉽게 발견할 수 있습니다.

이 네 가지(계정, 키, 기본 URL, 모델 ID)가 준비되면 요청 형태에 필요한 모든 것이 갖춰진 것입니다. curl 방식은 어떤 SDK도 설치할 필요가 없고, Python 방식도 이미 가지고 있을 가능성이 높은 HTTP 클라이언트만 있으면 됩니다.

빠른 환경 점검

사용할 셸에서 키와 기본 URL이 보이는지 확인하세요:

[ -n "$HIAPI_API_KEY" ] && echo "key set"
[ -n "$HIAPI_BASE_URL" ] && echo "base url set"

두 줄이 모두 출력되면 다음 섹션으로 넘어갈 수 있습니다.

HiAPI에서 grok-4.7 요청의 형태

이 튜토리얼의 모든 요청은 동일한 네 가지 요소를 공유합니다: 엔드포인트, 인증 헤더, 모델 필드, messages 배열입니다. 이들을 각각 이해하면 이후 스트리밍과 도구 호출을 훨씬 쉽게 이해할 수 있습니다. 이 기능들은 이 형태의 일부만 바꿀 뿐이기 때문입니다.

엔드포인트

이 호출은 기본 URL 아래의 채팅 완성 엔드포인트로 POST 요청을 보냅니다. 이 튜토리얼에서는 다음과 같이 표현합니다:

POST {HIAPI_BASE_URL}/v1/chat/completions

대시보드에 표시된 경로가 다르다면 대시보드의 경로를 사용하세요. 어느 쪽이든 요청 본문은 동일합니다.

인증 헤더

인증은 Authorization 헤더의 bearer 토큰으로 이루어집니다:

Authorization: Bearer $HIAPI_API_KEY

쿼리 파라미터가 아닌 요청 헤더이므로, 키가 URL이나 접근 로그, 브라우저 기록에 나타나지 않습니다.

콘텐츠 타입

요청 본문은 JSON이므로 Content-Type: application/json을 보냅니다. curl과 어떤 Python HTTP 클라이언트든 이를 대신 설정해 주지만, 헤더 집합에 모호함이 없도록 명시적으로 지정하는 편이 좋습니다.

요청 본문: model과 messages

요청 본문은 최소 두 개의 필드를 포함하는 JSON 객체입니다:

  • model — 모델 식별자 문자열이며, 여기서는 grok-4.7입니다.
  • messages — 순서가 있는 메시지 객체 배열입니다. 각 객체는 role과 content를 가집니다. 자주 쓰는 역할은 system(대화를 틀 짓는 지침), user(요청 자체), assistant(대화를 이어갈 때 이전 모델 턴을 제공)입니다.

최소 요청 본문은 다음과 같습니다:

{
  "model": "grok-4.7",
  "messages": [
    { "role": "system", "content": "You are a concise technical assistant." },
    { "role": "user", "content": "Explain what an idempotent HTTP method is in two sentences." }
  ]
}

이것이 기본 호출의 전부입니다. 스트리밍이나 도구 정의 같은 다른 모든 것은 같은 객체 위의 추가 필드일 뿐입니다. 이것이 앞의 섹션들이 중요한 이유입니다. 이 형태만 올바르면 나머지는 추가 사항일 뿐입니다.

응답의 모습

응답은 JSON 객체입니다. 거의 항상 원하는 부분은 choices 아래에 있으며 배열입니다. 각 항목은 role과 content를 가진 message를 포함합니다. 그 밖에 요청을 처리한 모델과 토큰 수를 설명하는 usage 블록 같은 메타데이터도 있습니다. choices[0].message.content를 읽으면 답변 텍스트를 얻습니다. 다른 필드는 로깅과 비용 추적에 유용하지만 입문 단계에서는 필요하지 않습니다.

실제로 동작하는 curl 요청

키를 내보내고 요청 형태를 이해했다면 curl 호출은 짧습니다. 문제를 애플리케이션 코드와 분리하고 싶을 때 복사할 버전이 바로 이것입니다.

curl -sS "$HIAPI_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-4.7",
    "messages": [
      { "role": "system", "content": "You are a concise technical assistant." },
      { "role": "user", "content": "In one sentence, what is a bearer token?" }
    ]
  }'

각 플래그의 역할

  • -sS는 출력을 조용하게 유지하지만 오류는 계속 출력합니다. 이것이 없으면 실패한 요청이 빈 응답처럼 보일 수 있습니다.
  • URL은 기본 URL 변수에 채팅 완성 경로를 붙여 구성하므로 스크립트에 호스트를 하드코딩할 필요가 없습니다.
  • -H "Authorization: ..."는 bearer 토큰을 보냅니다. 변수는 셸이 확장하므로 앞서 내보내는 것이 중요했습니다.
  • -H "Content-Type: application/json"은 서버에 요청 본문이 JSON임을 알립니다.
  • -d '...'는 요청 본문입니다. JSON을 감싼 작은따옴표는 셸이 그 안의 중괄호와 따옴표를 해석하지 못하게 합니다.

요청 본문을 파일에 넣는 편이 낫다면 body.json에 작성하고 -d 인자를 -d @body.json으로 바꾸세요. messages 배열이 길어지면 대개 이쪽이 더 명확합니다.

원시 JSON 읽기

응답은 JSON 객체로 표준 출력에 도착합니다. 눈으로 확인하고 싶을 때는 포매터를 통해 파이프하세요:

curl -sS "$HIAPI_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json | python -m json.tool

choices 배열이 보이고 그 첫 요소에 content 문자열을 가진 message 객체가 있어야 합니다. 그 문자열이 모델의 답변입니다. 그와 나란히 보통 응답 식별자, 에코된 모델 이름, 그리고 프롬프트와 완성 토큰 수를 담은 usage 객체도 있습니다. 지금 성공한 응답을 하나 기록해 두세요. Python 클라이언트를 검증할 때 비교할 기준이 됩니다.

이 단계의 흔한 실패

401은 거의 항상 bearer 토큰이 없거나, 형식이 잘못되었거나, 실제로 현재 셸에 내보내지지 않았음을 의미합니다. 요청 본문에 관한 메시지와 함께 오는 400은 보통 JSON 형식 오류를 의미합니다. 쉼표 뒤에 붙은 추가 쉼표나 문서에서 붙여넣은 스마트 따옴표가 흔한 원인입니다. 다른 것을 살펴보기 전에 이 두 가지를 고치세요.

같은 요청을 Python으로 구현하기

Python 버전은 같은 요청 본문으로 같은 일을 합니다. 다시 작성하는 의의는 셸 테스트에서 애플리케이션에 넣을 코드로 넘어갈 때 요청 본문이 따옴표로 감싼 문자열이 아니라 데이터 구조가 된다는 점입니다.

표준 라이브러리 사용

의존성을 추가하고 싶지 않다면 urllib이면 충분합니다:

import json
import os
import urllib.request

api_key = os.environ["HIAPI_API_KEY"]
base_url = os.environ["HIAPI_BASE_URL"]

payload = {
    "model": "grok-4.7",
    "messages": [
        {"role": "system", "content": "You are a concise technical assistant."},
        {"role": "user", "content": "In one sentence, what is a bearer token?"},
    ],
}

request = urllib.request.Request(
    f"{base_url}/v1/chat/completions",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    },
    method="POST",
)

with urllib.request.urlopen(request) as response:
    body = json.loads(response.read().decode("utf-8"))

print(body["choices"][0]["message"]["content"])

requests 사용

환경에 requests가 이미 있다면 호출이 더 짧습니다:

import os
import requests

response = requests.post(
    f"{os.environ['HIAPI_BASE_URL']}/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
    json={
        "model": "grok-4.7",
        "messages": [
            {"role": "system", "content": "You are a concise technical assistant."},
            {"role": "user", "content": "In one sentence, what is a bearer token?"},
        ],
    },
    timeout=60,
)
response.raise_for_status()
print(response.json()["choices"][0]["message"]["content"])

이 버전이 애플리케이션에 더 안전한 이유

세 가지 의도적인 선택에 주목하세요. 키와 기본 URL은 환경 변수에서 오며 소스 코드의 리터럴에서 오지 않습니다. 요청 본문은 Python 딕셔너리이므로 위치가 잘못된 따옴표는 혼란스러운 서버 측 거부 대신 즉시 SyntaxError를 냅니다. 그리고 호출에는 타임아웃이 설정되어 있습니다. 타임아웃이 없으면 멈춘 연결이 워커 프로세스를 무기한 매달리게 할 수 있습니다.

curl에서 사용한 것과 같은 프롬프트로 Python 버전을 실행하세요. 답변의 문구는 실행마다 조금씩 다를 수 있습니다. 모델은 결정적이지 않기 때문입니다. 하지만 응답 구조는 완전히 같아야 합니다. choices 배열, content를 가진 message, usage 블록입니다. 구조가 일치하면 클라이언트가 올바르게 연결된 것이며 나머지 섹션은 선택적 개선입니다.

스트리밍 켜기

스트리밍은 토큰이 도착하는 방식을 바꿀 뿐 요청하는 내용을 바꾸지 않습니다. 전체 답변을 기다리는 대신 점진적으로 받게 되며, 이것이 채팅 인터페이스가 빠르게 반응하는 것처럼 느껴지는 이유입니다.

요청의 변경

이미 가진 요청 본문에 필드를 하나 추가하세요:

{
  "model": "grok-4.7",
  "stream": true,
  "messages": [
    { "role": "user", "content": "Count from one to five, one number per line." }
  ]
}

엔드포인트, 헤더, messages의 다른 부분은 전혀 바뀌지 않습니다. 기본 형태를 먼저 배우는 의의가 바로 이것입니다. 스트리밍은 추가 플래그이지 두 번째 API가 아닙니다.

반환되는 것

stream을 켜면 응답은 단일 JSON 객체가 아닙니다. 서버 전송 이벤트(SSE)의 연속입니다. 각 이벤트는 한 줄의 data:를 가지며, 그 페이로드는 일반 응답과 같은 형태의 JSON 조각입니다. 다만 choices[0]에 완전한 message 대신 delta 객체가 들어 있습니다. 초기 조각은 역할만 포함할 수 있고, 중간 조각은 내용 조각을 담으며, 스트림은 data: [DONE] 센티널 줄로 끝납니다.

클라이언트로서 당신의 일은 delta.content 문자열을 순서대로 이어 붙이고 센티널에서 멈추는 것입니다. content가 없는 조각은 오류가 아니라 아무 동작도 하지 않는 것으로 처리하세요.

Python에서 청크 소비하기

import json
import os
import requests

with requests.post(
    f"{os.environ['HIAPI_BASE_URL']}/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
    json={
        "model": "grok-4.7",
        "stream": True,
        "messages": [{"role": "user", "content": "Count from one to five."}],
    },
    stream=True,
    timeout=60,
) as response:
    response.raise_for_status()
    for line in response.iter_lines():
        if not line:
            continue
        line = line.decode("utf-8")
        if not line.startswith("data: "):
            continue
        data = line[len("data: "):]
        if data == "[DONE]":
            break
        delta = json.loads(data)["choices"][0].get("delta", {})
        text = delta.get("content")
        if text:
            print(text, end="", flush=True)

두 가지 세부 사항이 스트리밍이 제대로 동작할지 아니면 멈춘 것처럼 보일지를 결정합니다. HTTP 클라이언트에 stream=True를 넘겨 전체 응답 본문을 버퍼링하지 않게 하고, 응답을 한 번에 읽는 대신 줄 단위로 반복하세요. print의 flush=True는 화면에서 보이는 것에만 관련됩니다. 이것이 없으면 청크가 도착하는 대로 처리되더라도 출력이 마지막에 한꺼번에 터진 것처럼 보일 수 있습니다.

스트리밍을 따로 검증하기

같은 테스트에서 스트리밍과 도구 호출을 동시에 검증하지 마세요. 먼저 이어 붙인 텍스트가 간단한 프롬프트에 대해 합리적인 답변을 주는지 확인한 다음 기능을 하나씩 추가하세요. 스트림이 센티널 없이 끝났다면 그것은 모델이 아니라 연결을 점검하라는 신호입니다.

도구 호출 옵션

도구 호출은 모델이 텍스트만 생성하는 것이 아니라 코드에 정의한 함수를 요청할 수 있게 합니다. 요청 측에서 추가하는 방식은 스트리밍과 완전히 같습니다.

옵션을 두는 위치

도구 정의는 요청 본문의 tools 배열입니다. 각 항목은 호출 가능한 대상 하나를 설명합니다: 이름, 언제 사용하는지에 대한 설명, 그리고 매개변수의 JSON schema 설명입니다. 설명은 보이는 것보다 더 중요합니다. 모델이 도구가 언제 적절한지 판단하는 유일한 안내이기 때문입니다.

{
  "model": "grok-4.7",
  "messages": [
    { "role": "user", "content": "What is the weather in Lisbon right now?" }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a named city.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": { "type": "string", "description": "City name, e.g. Lisbon" }
          },
          "required": ["city"]
        }
      }
    }
  ]
}

기억할 형태는 type 더하기 function이며, 그 안에 name, description, parameters가 중첩됩니다. 이미 JSON schema 정의가 있다면 parameters 블록이 그것을 넣을 곳입니다.

모델이 응답하는 방식

모델이 도구가 필요하다고 판단하면 choices[0]의 assistant 메시지는 일반 텍스트가 아닙니다. 그 content는 보통 비어 있고 tool_calls 배열을 가집니다. 각 항목은 식별자, 당신의 정의 중 하나와 일치하는 함수 이름, 그리고 JSON을 담은 arguments 문자열을 가집니다. 이 arguments 문자열을 파싱하세요. 그것은 객체가 아니라 문자열이며, 객체로 취급하는 것이 흔한 오류 원인입니다.

이후 코드가 그 함수를 실행하고 후속 요청을 보냅니다: 원래 대화, tool_calls가 있는 assistant 메시지, 그리고 호출 식별자와 결과를 포함하며 역할이 tool인 새 메시지입니다. 모델은 그 결과를 사용해 최종 답변을 생성합니다. tool_call_id는 assistant 메시지의 식별자와 일치해야 합니다. 이것이 없으면 후속 요청은 모호해집니다.

실용적 보호 조치

  • 파싱한 매개변수를 보낸 것과 같은 schema로 검증하세요. 모델은 schema의 안내를 받지만 그것에 의해 제약되지는 않습니다.
  • 반환된 함수 이름을 신뢰할 수 없는 입력으로 취급하고 명시적인 함수 허용 목록으로만 라우팅하세요.
  • 첫 테스트에서 stream과 도구를 동시에 켜지 마세요. 먼저 비스트리밍 도구 왕복을 끝에서 끝까지 확인한 다음, 사용 사례에 둘 다 필요하다면 결합하세요.

출시 전 응답 검증

터미널에서 동작하는 한 번의 호출은 검증된 통합과 같지 않습니다. 이 체크리스트를 끝까지 따라가고, 그런 다음 스크립트를 스모크 테스트로 남겨 두세요.

전송 계층 확인

무엇이든 파싱하기 전에 HTTP 상태가 200인지 확인하세요. 먼저 파싱하는 클라이언트는 실제 문제가 401일 때 혼란스러운 KeyError를 냅니다. 상태를 명시적으로 확인하고, 실패하면 응답 본문과 함께 기록하세요. 서버는 보통 어디가 잘못되었는지 알려줍니다.

응답 구조 확인

가정하지 말고 확인하세요. 성공한 비스트리밍 호출에서 세 가지를 검증하세요: choices가 존재하고 비어 있지 않은지, choices[0].message.content가 비어 있지 않은 문자열인지, 응답에 에코된 모델이 보낸 것과 일치하는지. 마지막 확인은 놀랄 만큼 많은 구성 오류를 잡아냅니다. 기본 모델이 요청한 모델을 덮어쓰는 경우입니다.

오류 경로를 의도적으로 연습하기

장애가 발생할 때까지 코드의 동작을 모른 채 기다리지 마세요. 세 가지 빠른 부정 테스트를 실행하세요:

  • 인증 헤더를 제거하고 호출하여 코드가 무한 재시도가 아니라 명확한 인증 오류를 드러내는지 확인하세요.
  • model 필드가 없는 요청 본문을 보내 클라이언트 오류를 받는지, 멈추지 않는지 확인하세요.
  • 의도적으로 매우 긴 프롬프트를 보내 타임아웃과 잘림 처리가 예상대로 동작하는지 확인하세요.

각각은 막연한 “문제가 발생했습니다”가 아니라 구체적이고 기록된 실패를 내야 합니다.

개선 기능을 따로 검증하기

스트리밍을 켰다면 이어 붙인 청크가 일관된 답변을 이루고 센티널이 루프를 끝내는지 확인하세요. 도구를 켰다면 왕복이 완료되는지 확인하세요: 도구 호출이 파싱되고, 함수가 실행되고, 결과가 일치하는 식별자와 함께 반환되어 최종 텍스트 답변이 생성됩니다. 각 검증을 각자의 작은 스크립트에 두면 실패가 특정 기능을 가리킵니다.

점검을 반복 가능하게 만들기

전체 순서를 하나의 파일에 넣고, 어떤 확인이든 실패하면 0이 아닌 종료 코드로 종료하며, 배포 전마다 스테이징 구성에 대해 실행하세요. 몇 초면 충분해야 하며, 기본 호출, 스트리밍 호출 하나, 도구를 사용한다면 도구 왕복 한 번을 포함합니다. 이것이 통과하면 프로덕션의 실패는 하부 파이프라인이 아니라 보내는 요청에 있다고 믿을 수 있습니다.

확인이 통과하면 curl 호출을 한 번 더 실행하세요. 원시 JSON이 애플리케이션 출력과 일치하면 통합이 완료된 것입니다.

영구 AI 아티팩트로 빌드 시작하기

API 키를 받아 태스크 기반의 하나의 API로 이미지, 비디오, 오디오 모델을 호출하세요.

API 문서 보기