API

API Key

작성일 2026.02.27 | 수정일 2026.03.25

REST API를 요청할 때 HTTP 헤더에 Authorization 정보를 추가하여 인증받을 수 있습니다. API를 요청한 계정의 소유자를 확인하는데 필수적인 절차입니다.

솔라피 API Key 관리 페이지에서 API Key와 API Secret을 생성하여 REST API 호출에 사용하실 수 있습니다.
Authorization 헤더 정보를 전달하는 방식은 HTTP에서 인증을 위한 수단으로 널리 사용되고 있습니다.
Basic access authentication을 참고하세요.

Authorization 헤더 형식

Authorization: <AuthenticationMethod> apiKey=<API Key>, date=<Date Time>, salt=<Salt>, signature=<Signature>
curl -X GET https://api.solapi.com/messages/v4/list \
  --header "Authorization: HMAC-SHA256 apiKey=NCSAYU7YDBXYORXC, date=2019-07-01T00:41:48Z, salt=jqsba2jxjnrjor, signature=1779eac71a24cbeeadfa7263cb84b7ea0af1714f5c0270aa30ffd34600e363b4"

파라미터 설명

파라미터제약사항설명
AuthenticationMethodHMAC-SHA256, HMAC-MD5 중 선택Signature 생성 알고리즘
API Key솔라피 콘솔에서 생성발급받은 API Key
Date TimeISO 8601 규격 (예: 2019-07-01T00:41:48Z)요청 시간
Salt12~64바이트의 불규칙적인 문자열랜덤 문자열
SignatureDate Time + Salt를 데이터로 하고 API Secret을 Key로 생성

Signature 생성 방법

Signature는 다음과 같은 방식으로 생성됩니다

  1. 데이터 준비: <Date Time> + <Salt> 문자열 연결

  2. HMAC 생성: API Secret을 Key로 사용하여 HMAC 해시 생성

  3. 알고리즘: HMAC-SHA256 또는 HMAC-MD5 선택 가능

error

API Secret은 Signature 생성 시에만 사용하고 외부에 노출되지 않도록 주의하세요.

보안 제약사항

제약사항목적Description
시간 제한Replay Attack 방지서버 시간 기준 ±15분 이내
중복 방지재사용 공격 차단15분 내 동일한 Signature 재사용 불가
Salt 변경Signature 고유성 보장매 요청마다 다른 Salt 사용 필수

메시지의 무결성 검증을 위해 Hash 기반의 MAC 알고리즘을 사용합니다.
Hash-based message authentication code를 참고하세요.


오류 처리

API 인증 과정에서 발생할 수 있는 오류들과 대응 방법입니다.

에러 코드HTTP 상태 코드해결 방법설명
InvalidAPIKey403API Key 확인 및 재발급유효하지 않은 API Key
SignatureDoesNotMatch403Signature 생성 로직 검토Signature 불일치
RequestTimeTooSkewed403시스템 시간 동기화시간 차이 초과 (±15분)
DuplicatedSignature403Salt 값 변경 및 재시도중복된 Signature 사용
warning

서버와 클라이언트 간의 시간 차이가 15분을 초과하면 인증이 실패합니다. 시스템 시간을 정확히 동기화해 주세요.


언어별 구현 예제

Node.js 20.19.0
const crypto = require('crypto');

//	"""HMAC-SHA256 시그니처 생성"""
function generateSignature(apiSecret, dateTime, salt) {
  const data = dateTime + salt;
  return crypto
    .createHmac('sha256', apiSecret)
    .update(data)
    .digest('hex');
}

//	"""Authorization 헤더 생성"""
function createAuthHeader(apiKey, apiSecret) {
  const dateTime = new Date().toISOString();
  const salt = crypto.randomBytes(16).toString('hex');
  const signature = generateSignature(apiSecret, dateTime, salt);
  
  return `HMAC-SHA256 apiKey=${apiKey}, date=${dateTime}, salt=${salt}, signature=${signature}`;
}
const axios = require('axios');

async function sendMessage(apiKey, apiSecret, messageData) {
  const authHeader = createAuthHeader(apiKey, apiSecret);
  
  try {
    const response = await axios.post('https://api.solapi.com/messages/v4/send-many/detail', messageData, {
      headers: {
        'Authorization': authHeader,
        'Content-Type': 'application/json'
      }
    });
    
    return response.data;
  } catch (error) {
    console.error('API 요청 실패:', error.response?.data || error.message);
    throw error;
  }
}
Java 17+
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.format.DateTimeFormatter;

public class SolapiAuth {

//	"""HMAC-SHA256 시그니처 생성"""
	public static String generateSignature(String apiSecret, String dateTime, String salt)
		throws Exception {
		Mac mac = Mac.getInstance("HmacSHA256");
		mac.init(new SecretKeySpec(apiSecret.getBytes(), "HmacSHA256"));
		byte[] hash = mac.doFinal((dateTime + salt).getBytes());
		return HexFormat.of().formatHex(hash); 
	}
//	"""Authorization 헤더 생성"""
	public static String createAuthHeader(String apiKey, String apiSecret) throws Exception {
		String dateTime = Instant.now().toString();
		String salt = UUID.randomUUID().toString().replace("-", "");
		String signature = generateSignature(apiSecret, dateTime, salt);

		return "HMAC-SHA256 apiKey=%s, date=%s, salt=%s, signature=%s"
			.formatted(apiKey, dateTime, salt, signature);
	}
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public class SolapiClient {
    
    public static String sendMessage(String apiKey, String apiSecret, String messageJson) 
            throws Exception {
        String authHeader = SolapiAuth.createAuthHeader(apiKey, apiSecret);
        
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.solapi.com/messages/v4/send-many/detail"))
                .header("Authorization", authHeader)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(messageJson))
                .build();
        
        HttpResponse<String> response = client.send(request, 
                HttpResponse.BodyHandlers.ofString());
        
        return response.body();
    }
}
Kotlin 2.1
import java.time.Instant
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

object SolapiAuth {
    
    /**
     * HMAC-SHA256 시그니처 생성
     */
    @Throws(Exception::class)
    fun generateSignature(apiSecret: String, dateTime: String, salt: String): String {
        val mac = Mac.getInstance("HmacSHA256")
        mac.init(SecretKeySpec(apiSecret.toByteArray(), "HmacSHA256"))
        val hash = mac.doFinal((dateTime + salt).toByteArray())
        return HexFormat.of().formatHex(hash)
    }
    
    /**
     * Authorization 헤더 생성
     */
    @Throws(Exception::class)
    fun createAuthHeader(apiKey: String, apiSecret: String): String {
        val dateTime = Instant.now().toString()
        val salt = UUID.randomUUID().toString().replace("-", "")
        val signature = generateSignature(apiSecret, dateTime, salt)
        
        return "HMAC-SHA256 apiKey=$apiKey, date=$dateTime, salt=$salt, signature=$signature"
    }
}
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

object SolapiClient {
    
    @Throws(Exception::class)
    fun sendMessage(apiKey: String, apiSecret: String, messageJson: String): String {
        val authHeader = SolapiAuth.createAuthHeader(apiKey, apiSecret)
        
        val client = HttpClient.newHttpClient()
        val request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.solapi.com/messages/v4/send-many/detail"))
            .header("Authorization", authHeader)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(messageJson))
            .build()
        
        val response = client.send(request, HttpResponse.BodyHandlers.ofString())
        
        return response.body()
    }
}
Python 3.12.0
import hmac
import hashlib
import secrets
from datetime import datetime, timezone

def generate_signature(api_secret: str, date_time: str, salt: str) -> str:
    """HMAC-SHA256 시그니처 생성"""
    data = date_time + salt
    signature = hmac.new(
        api_secret.encode(),
        data.encode(),
        hashlib.sha256
    ).hexdigest()
    return signature

def create_auth_header(api_key: str, api_secret: str) -> str:
    """Authorization 헤더 생성"""
    date_time = datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
    salt = secrets.token_hex(16)
    signature = generate_signature(api_secret, date_time, salt)
    
    return f"HMAC-SHA256 apiKey={api_key}, date={date_time}, salt={salt}, signature={signature}"
import requests
from typing import Dict, Any

def send_message(api_key: str, api_secret: str, message_data: Dict[str, Any]) -> Dict[str, Any]:
    """메시지 전송 API 호출"""
    auth_header = create_auth_header(api_key, api_secret)
    
    headers = {
        'Authorization': auth_header,
        'Content-Type': 'application/json'
    }
    
    response = requests.post(
        'https://api.solapi.com/messages/v4/send-many/detail',
        json=message_data,
        headers=headers
    )
    
    response.raise_for_status()
    return response.json()

if __name__ == "__main__":
    message_data = {
        "message": {
            "to": "01012345678",
            "from": "01087654321",
            "text": "테스트 메시지입니다."
        }
    }
PHP 8.4.11
<?php

class SolapiAuth {
    // HMAC-SHA256 시그니처 생성
    public static function generateSignature($apiSecret, $dateTime, $salt) {
        $data = $dateTime . $salt;
        return hash_hmac('sha256', $data, $apiSecret);
    }
    
    // Authorization 헤더 생성
    public static function createAuthHeader($apiKey, $apiSecret) {
        $dateTime = gmdate('Y-m-d\TH:i:s\Z');
        $salt = bin2hex(random_bytes(16));
        $signature = self::generateSignature($apiSecret, $dateTime, $salt);
        
        return "HMAC-SHA256 apiKey={$apiKey}, date={$dateTime}, salt={$salt}, signature={$signature}";
    }
}

?>
<?php

function sendMessage($apiKey, $apiSecret, $messageData) {
    $authHeader = SolapiAuth::createAuthHeader($apiKey, $apiSecret);
    
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => 'https://api.solapi.com/messages/v4/send-many/detail',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($messageData),
        CURLOPT_HTTPHEADER => [
            'Authorization: ' . $authHeader,
            'Content-Type: application/json'
        ]
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode !== 200) {
        throw new Exception("API 요청 실패: HTTP {$httpCode}");
    }
    
    return json_decode($response, true);
}

?>
Go 1.24.6
package main

import (
    "crypto/hmac"
    "crypto/rand"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "time"
)

// HMAC-SHA256 시그니처 생성
func generateSignature(apiSecret, dateTime, salt string) string {
    data := dateTime + salt
    h := hmac.New(sha256.New, []byte(apiSecret))
    h.Write([]byte(data))
    return hex.EncodeToString(h.Sum(nil))
}

// Authorization 헤더 생성
func createAuthHeader(apiKey, apiSecret string) (string, error) {
    dateTime := time.Now().UTC().Format("2006-01-02T15:04:05Z")
    
    saltBytes := make([]byte, 16)
    if _, err := rand.Read(saltBytes); err != nil {
        return "", err
    }
    salt := hex.EncodeToString(saltBytes)
    
    signature := generateSignature(apiSecret, dateTime, salt)
    
    return fmt.Sprintf("HMAC-SHA256 apiKey=%s, date=%s, salt=%s, signature=%s",
        apiKey, dateTime, salt, signature), nil
}
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

type MessageData struct {
    Message struct {
        To   string `json:"to"`
        From string `json:"from"`
        Text string `json:"text"`
    } `json:"message"`
}

func sendMessage(apiKey, apiSecret string, messageData MessageData) map[string]interface{} {
    authHeader := createAuthHeader(apiKey, apiSecret)
    
    jsonData, _ := json.Marshal(messageData)
    
    req, _ := http.NewRequest("POST", "https://api.solapi.com/messages/v4/send-many/detail", bytes.NewBuffer(jsonData))
    req.Header.Set("Authorization", authHeader)
    req.Header.Set("Content-Type", "application/json")
    
    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    
    body, _ := io.ReadAll(resp.Body)
    
    var result map[string]interface{}
    json.Unmarshal(body, &result)
    
    return result
}\