TL;DR
Etsy API는 개발자가 Etsy 마켓플레이스와 통합된 애플리케이션을 구축할 수 있도록 OAuth 2.0 인증, 상점/상품/주문/재고 관리를 위한 RESTful 엔드포인트를 제공합니다. 앱당 초당 10회 호출 제한이 있으니 주의하세요. 본 글에서는 인증, 주요 엔드포인트, 웹훅, 프로덕션 배포 전략까지 실전 구현 단계별로 안내합니다.
💡 Apidog는 API 테스트 자동화에 특화된 도구입니다. Etsy 엔드포인트 테스트, OAuth 흐름 검증, 웹훅 페이로드 검사, 인증 문제 디버깅 등 API 통합 과정을 한 곳에서 빠르게 실험할 수 있습니다. API 사양 가져오기, 응답 모킹, 팀과의 공유도 간편합니다.
소개
Etsy는 230개국 이상에서 연간 130억 달러 이상의 거래를 처리합니다. 개발자라면 전자상거래 도구, 재고 관리, 분석 플랫폼 등에 Etsy API를 반드시 통합해야 효율적인 자동화를 구현할 수 있습니다.
여러 마켓을 운영하는 판매자들은 수동 데이터 입력으로 주당 15~20시간을 허비하고 있습니다. Etsy API를 제대로 연결하면 상품 동기화, 주문 처리, 재고 업데이트를 자동화하여 업무 시간을 대폭 줄일 수 있습니다.
이 가이드에서는 OAuth 2.0 인증, 상점 및 상품 관리, 주문 처리, 웹훅 처리, 장애 대응까지 실제 프로덕션 수준의 Etsy API 통합 절차를 단계별로 다룹니다.
Etsy API란 무엇인가요?
Etsy 개발자 문서를 확인하면, Etsy API는 다음과 같은 기능을 제공합니다.
- 상점 및 프로필 정보 조회
- 상품 등록, 수정, 재고 관리
- 주문 처리 및 배송 추적
- 고객/거래 데이터 접근
- 배송 프로필, 세금 계산
- 이미지 및 미디어 업로드
주요 기능
| 기능 | 설명 |
|---|---|
| RESTful 디자인 | 표준 HTTP 메서드, JSON 응답 사용 |
| OAuth 2.0 | 액세스 토큰 갱신 방식의 보안 인증 |
| 웹훅 | 주문, 상품 등 이벤트에 대한 실시간 알림 |
| 속도 제한 | 앱당 초당 10회 요청 (버스트 허용량 별도) |
| 샌드박스 지원 | 테스트 전용 환경 제공 (실시간 데이터 없이 개발 가능) |
API 아키텍처 개요
Etsy는 버전별 REST API 구조를 사용합니다.
https://openapi.etsy.com/v3/application/
현재 표준은 v3이며, OAuth 2.0과 간결한 엔드포인트 구조를 지원합니다.
API 버전 비교
| 버전 | 상태 | 인증 | 사용 사례 |
|---|---|---|---|
| V3 | 현재 | OAuth 2.0 | 신규 통합 |
| V2 | 지원 종료 예정 | OAuth 1.0a | 레거시 앱 |
| V1 | 지원 종료 | 해당 없음 | 사용 금지 |
V2에서 V3로 반드시 마이그레이션 하세요. V2는 2026년 말 완전 종료됩니다.
시작하기: 인증 설정
1단계: Etsy 개발자 계정 생성
- Etsy 개발자 포털 방문
- Etsy 계정으로 로그인(없으면 신규 생성)
- 개발자 대시보드 > 내 앱 이동
- 새 앱 생성 클릭
2단계: 애플리케이션 등록
- 앱 이름: OAuth 인증시 사용자에게 노출
- 앱 설명: 주요 기능 및 사용자 설명
- 리디렉션 URI: 인증 후 사용자가 돌아올 HTTPS 주소
- 프로덕션/개발: 처음엔 개발 모드로 시작
등록 후 아래 정보를 받습니다.
- 키 문자열: 공개 API 식별자
- 공유 비밀: 비공개 API 비밀번호 (노출 금지)
보안: 환경 변수로 관리하세요.
# .env 파일 예시
ETSY_KEY_STRING="your_key_string_here"
ETSY_SHARED_SECRET="your_shared_secret_here"
ETSY_ACCESS_TOKEN="generated_via_oauth"
ETSY_REFRESH_TOKEN="generated_via_oauth"
3단계: OAuth 2.0 흐름 이해
- 사용자가 앱에서 "Etsy 연결" 클릭
- 앱이 Etsy 인증 URL로 리디렉션
- 사용자가 로그인/권한 부여
- 인증 코드가 콜백으로 전달됨
- 앱이 코드로 토큰 교환
- 토큰으로 API 호출
- 토큰 만료(1시간 후)시 refresh_token으로 갱신
4단계: OAuth 권한 부여 구현
const generateAuthUrl = (clientId, redirectUri, state) => {
const baseUrl = 'https://www.etsy.com/oauth/connect';
const params = new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
scope: 'listings_r listings_w orders_r orders_w shops_r',
state: state, // CSRF 방지용 난수
response_type: 'code'
});
return `${baseUrl}?${params.toString()}`;
};
// 사용 예시
const authUrl = generateAuthUrl(
process.env.ETSY_KEY_STRING,
'https://your-app.com/callback',
crypto.randomBytes(16).toString('hex')
);
console.log(`사용자를 이 URL로 리디렉션: ${authUrl}`);
필수 스코프
| 스코프 | 설명 | 사용 사례 |
|---|---|---|
listings_r |
상품 목록 읽기 | 상품 조회, 재고 동기화 |
listings_w |
상품 목록 쓰기 | 상품 생성/수정 |
orders_r |
주문 읽기 | 주문 관리, 처리 |
orders_w |
주문 쓰기 | 상태 변경, 추적 추가 |
shops_r |
상점 정보 읽기 | 프로필, 분석 표시 |
transactions_r |
거래 정보 읽기 | 재무 보고 |
email |
구매자 이메일 접근 | 주문 커뮤니케이션 |
5단계: 코드와 액세스 토큰 교환
const exchangeCodeForToken = async (code, redirectUri) => {
const response = await fetch('https://api.etsy.com/v3/public/oauth/token', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: process.env.ETSY_KEY_STRING,
client_secret: process.env.ETSY_SHARED_SECRET,
redirect_uri: redirectUri,
code: code
})
});
const data = await response.json();
return {
access_token: data.access_token,
refresh_token: data.refresh_token,
expires_in: data.expires_in, // 보통 3600초
user_id: data.user_id,
scope: data.scope
};
};
// 콜백 라우트 처리 예시
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
if (state !== req.session.oauthState) {
return res.status(400).send('Invalid state parameter');
}
try {
const tokens = await exchangeCodeForToken(code, 'https://your-app.com/callback');
await db.users.update(req.session.userId, {
etsy_access_token: tokens.access_token,
etsy_refresh_token: tokens.refresh_token,
etsy_token_expires: Date.now() + (tokens.expires_in * 1000),
etsy_user_id: tokens.user_id
});
res.redirect('/dashboard');
} catch (error) {
console.error('Token exchange failed:', error);
res.status(500).send('Authentication failed');
}
});
6단계: 토큰 갱신 구현
const refreshAccessToken = async (refreshToken) => {
const response = await fetch('https://api.etsy.com/v3/public/oauth/token', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: process.env.ETSY_KEY_STRING,
client_secret: process.env.ETSY_SHARED_SECRET,
refresh_token: refreshToken
})
});
const data = await response.json();
return {
access_token: data.access_token,
refresh_token: data.refresh_token,
expires_in: data.expires_in
};
};
// API 호출 전 토큰 자동 유효성 체크
const ensureValidToken = async (userId) => {
const user = await db.users.findById(userId);
if (user.etsy_token_expires < Date.now() + 300000) { // 5분 이내 만료
const newTokens = await refreshAccessToken(user.etsy_refresh_token);
await db.users.update(userId, {
etsy_access_token: newTokens.access_token,
etsy_refresh_token: newTokens.refresh_token,
etsy_token_expires: Date.now() + (newTokens.expires_in * 1000)
});
return newTokens.access_token;
}
return user.etsy_access_token;
};
7단계: 인증된 API 호출
const makeEtsyRequest = async (endpoint, options = {}) => {
const accessToken = await ensureValidToken(options.userId);
const response = await fetch(`https://openapi.etsy.com/v3/application${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${accessToken}`,
'x-api-key': process.env.ETSY_KEY_STRING,
'Accept': 'application/json',
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Etsy API Error: ${error.message}`);
}
return response.json();
};
상점 관리 엔드포인트
상점 정보 조회
const getShopInfo = async (shopId) => {
const response = await makeEtsyRequest(`/shops/${shopId}`, {
method: 'GET'
});
return response;
};
// 사용 예시
const shop = await getShopInfo(12345678);
console.log(`Shop: ${shop.title}`);
console.log(`Currency: ${shop.currency_code}`);
console.log(`Listings count: ${shop.num_listings_active}`);
예시 응답
{
"shop_id": 12345678,
"shop_name": "MyHandmadeShop",
"title": "Handmade Jewelry & Accessories",
"announcement": "Welcome! Free shipping on orders over $50",
"currency_code": "USD",
"is_vacation": false,
"num_listings_active": 127
// ...
}
상점 섹션 검색
const getShopSections = async (shopId) => {
const response = await makeEtsyRequest(`/shops/${shopId}/sections`, {
method: 'GET'
});
return response;
};
상품 목록 관리
신규 상품 등록
const createListing = async (shopId, listingData) => {
const payload = {
title: listingData.title,
description: listingData.description,
price: listingData.price.toString(),
quantity: listingData.quantity,
sku: listingData.sku || [],
tags: listingData.tags.slice(0, 13),
category_id: listingData.categoryId,
shop_section_id: listingData.sectionId,
state: listingData.state || 'active',
who_made: listingData.whoMade,
when_made: listingData.whenMade,
is_supply: listingData.isSupply,
item_weight: listingData.weight || null,
item_weight_unit: listingData.weightUnit || 'g',
item_length: listingData.length || null,
item_width: listingData.width || null,
item_height: listingData.height || null,
item_dimensions_unit: listingData.dimensionsUnit || 'mm',
is_private: listingData.isPrivate || false,
recipient: listingData.recipient || null,
occasion: listingData.occasion || null,
style: listingData.style || []
};
const response = await makeEtsyRequest(`/shops/${shopId}/listings`, {
method: 'POST',
body: JSON.stringify(payload)
});
return response;
};
// 예시
const listing = await createListing(12345678, {
title: 'Sterling Silver Moon Phase Necklace',
description: 'Handcrafted sterling silver necklace featuring moon phases...',
price: 89.99,
quantity: 15,
sku: ['MOON-NECKLACE-001'],
tags: ['moon necklace', 'sterling silver', 'moon phase', 'celestial jewelry'],
categoryId: 10623,
sectionId: 12345,
state: 'active',
whoMade: 'i_did',
whenMade: 'made_to_order',
isSupply: false,
weight: 25,
weightUnit: 'g'
});
상품 이미지 업로드
const uploadListingImage = async (listingId, imagePath, imagePosition = 1) => {
const fs = require('fs');
const imageBuffer = fs.readFileSync(imagePath);
const base64Image = imageBuffer.toString('base64');
const payload = {
image: base64Image,
listing_image_id: null,
position: imagePosition,
is_watermarked: false,
alt_text: 'Handcrafted sterling silver moon phase necklace'
};
const response = await makeEtsyRequest(`/listings/${listingId}/images`, {
method: 'POST',
body: JSON.stringify(payload)
});
return response;
};
// 여러 이미지 업로드
const uploadListingImages = async (listingId, imagePaths) => {
const results = [];
for (let i = 0; i < imagePaths.length; i++) {
const result = await uploadListingImage(listingId, imagePaths[i], i + 1);
results.push(result);
}
return results;
};
상품 재고 업데이트
const updateListingInventory = async (shopId, listingId, inventory) => {
const payload = {
products: inventory.products.map(product => ({
sku: product.sku,
quantity: product.quantity
})),
is_over_selling: inventory.isOverSelling || false,
on_property: inventory.onProperty || []
};
const response = await makeEtsyRequest(
`/shops/${shopId}/listings/${listingId}/inventory`,
{
method: 'PUT',
body: JSON.stringify(payload)
}
);
return response;
};
// 사용 예시
await updateListingInventory(12345678, 987654321, {
products: [
{ sku: 'MOON-NECKLACE-001', quantity: 10 },
{ sku: 'MOON-NECKLACE-002', quantity: 5 }
],
isOverSelling: false
});
상품 목록 조회/삭제
const getListings = async (shopId, options = {}) => {
const params = new URLSearchParams({
limit: options.limit || 25,
offset: options.offset || 0
});
if (options.state) {
params.append('state', options.state);
}
const response = await makeEtsyRequest(
`/shops/${shopId}/listings?${params.toString()}`,
{ method: 'GET' }
);
return response;
};
const getListing = async (listingId) => {
const response = await makeEtsyRequest(`/listings/${listingId}`, {
method: 'GET'
});
return response;
};
const deleteListing = async (listingId) => {
const response = await makeEtsyRequest(`/listings/${listingId}`, {
method: 'DELETE'
});
return response;
};
주문 관리
주문 조회
const getOrders = async (shopId, options = {}) => {
const params = new URLSearchParams({
limit: options.limit || 25,
offset: options.offset || 0
});
if (options.status) {
params.append('status', options.status);
}
if (options.minLastModified) {
params.append('min_last_modified', options.minLastModified);
}
const response = await makeEtsyRequest(
`/shops/${shopId}/orders?${params.toString()}`,
{ method: 'GET' }
);
return response;
};
const getOrder = async (shopId, orderId) => {
const response = await makeEtsyRequest(`/shops/${shopId}/orders/${orderId}`, {
method: 'GET'
});
return response;
};
주문 상태 업데이트
const updateOrderStatus = async (shopId, orderId, trackingData) => {
const payload = {
carrier_id: trackingData.carrierId,
tracking_code: trackingData.trackingCode,
should_send_bcc_to_buyer: trackingData.notifyBuyer || true
};
const response = await makeEtsyRequest(
`/shops/${shopId}/orders/${orderId}/shipping`,
{
method: 'POST',
body: JSON.stringify(payload)
}
);
return response;
};
// 사용 예시
await updateOrderStatus(12345678, 1234567890, {
carrierId: 'usps',
trackingCode: '9400111899223456789012',
notifyBuyer: true
});
주요 배송업체 ID
| 배송업체 | 배송업체 ID |
|---|---|
| USPS | usps |
| FedEx | fedex |
| UPS | ups |
| DHL | dhl_express |
| 캐나다 포스트 | canada_post |
| 로열 메일 | royal_mail |
| 호주 포스트 | australia_post |
속도 제한 및 할당량
Etsy API 속도 제한
- 초당 10회 요청
- 버스트 허용량: 1초 내 최대 50회 짧은 버스트 가능
- 시간당 10,000회 호출 (앱당)
초과시 HTTP 429(Too Many Requests) 응답을 받게 됩니다.
지수 백오프 기반 재시도 예시
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await makeEtsyRequest(endpoint, options);
const remaining = response.headers.get('x-etsy-quota-remaining');
const resetTime = response.headers.get('x-etsy-quota-reset');
if (remaining < 100) {
console.warn(`Low quota remaining: ${remaining}, resets at ${resetTime}`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
속도 제한 관련 헤더
| 헤더 | 설명 |
|---|---|
x-etsy-quota-remaining |
남은 시간당 호출 수 |
x-etsy-quota-reset |
할당량 재설정 시각(Unix timestamp) |
x-etsy-limit-remaining |
남은 초당 호출 수 |
x-etsy-limit-reset |
초당 제한 재설정 시각 |
TIP: 이 헤더들을 반드시 로깅하여 모니터링하세요.
웹훅 통합
웹훅 구성 방법
- 개발자 대시보드 > 내 앱 이동
- 앱 선택
- 웹훅 추가 클릭
- HTTPS 엔드포인트 URL 입력
- 구독 이벤트 선택
주요 웹훅 이벤트
| 이벤트 유형 | 트리거 | 사용 사례 |
|---|---|---|
v3/shops/{shop_id}/orders/create |
새 주문 생성 | 주문처리, 확인서 발송 |
v3/shops/{shop_id}/orders/update |
주문 상태 변경 | 상태 동기화 |
v3/shops/{shop_id}/listings/create |
상품 목록 생성 | 외부 재고 업데이트 |
v3/shops/{shop_id}/listings/update |
상품 목록 수정 | 상품 데이터 동기화 |
v3/shops/{shop_id}/listings/delete |
상품 목록 삭제 | 외부 시스템 연동 삭제 |
웹훅 핸들러 예시
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/etsy', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-etsy-signature'];
const payload = req.body;
// 서명 검증
const isValid = verifyWebhookSignature(payload, signature, process.env.ETSY_WEBHOOK_SECRET);
if (!isValid) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
const event = JSON.parse(payload.toString());
switch (event.type) {
case 'v3/shops/*/orders/create':
await handleNewOrder(event.data);
break;
case 'v3/shops/*/orders/update':
await handleOrderUpdate(event.data);
break;
case 'v3/shops/*/listings/create':
await handleListingCreated(event.data);
break;
case 'v3/shops/*/listings/update':
await handleListingUpdated(event.data);
break;
case 'v3/shops/*/listings/delete':
await handleListingDeleted(event.data);
break;
default:
console.log('Unhandled event type:', event.type);
}
res.status(200).send('OK');
});
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
웹훅 모범 사례
- 서명 검증: 스푸핑 방지
- 200 OK 신속 반환: 5초 이내 미반환시 Etsy가 재전송
- 비동기 처리: 이벤트를 큐에 넣고 백그라운드 처리
- 멱등성: 중복 전달 방지
- 이벤트 로깅: 타임스탬프와 함께 감사 추적
일반적인 문제 해결
OAuth 토큰 교환 실패
- 증상: 인증 중 401/403 오류
- 진단 코드:
const error = await response.json();
console.error('OAuth error:', error);
-
해결책:
- 리디렉션 URI 완전 일치 여부 확인(https, 후행 슬래시 포함)
- client_id/client_secret 올바른지 확인
- 권한 부여 코드 만료 여부 확인(1회 사용, 5분 유효)
- 앱이 프로덕션 모드인지 체크
속도 제한 초과
- 증상: HTTP 429
-
해결책:
- 요청 대기열(rate limiter) 구현
- 지수 백오프 적용
- 요청 일괄 처리 사용(여러 상품 조회 등)
- 할당량 헤더 모니터링
class RateLimiter {
constructor(requestsPerSecond = 9) {
this.queue = [];
this.interval = 1000 / requestsPerSecond;
this.processing = false;
}
async add(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this.process();
});
}
async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const { requestFn, resolve, reject } = this.queue.shift();
try {
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
}
if (this.queue.length > 0) {
await new Promise(r => setTimeout(r, this.interval));
}
}
this.processing = false;
}
}
// 사용 예시
const etsyRateLimiter = new RateLimiter(9);
const result = await etsyRateLimiter.add(() => makeEtsyRequest('/shops/12345/listings'));
상품 목록 생성 실패(유효성 검사 오류)
- 증상: 400 Bad Request + 에러 메시지
-
원인:
- 잘못된 category_id (카테고리 API로 유효값 조회)
- price는 반드시 문자열
- 태그 13개 초과 금지
- 필수 필드 누락(title, description, price, quantity, who_made, when_made)
- 사전 검증 코드:
const validateListing = (data) => {
const errors = [];
if (!data.title || data.title.length < 5) {
errors.push('Title must be at least 5 characters');
}
if (typeof data.price !== 'string') {
errors.push('Price must be a string');
}
if (data.tags && data.tags.length > 13) {
errors.push('Maximum 13 tags allowed');
}
if (!['i_did', 'someone_else', 'collective'].includes(data.whoMade)) {
errors.push('Invalid who_made value');
}
return errors;
};
웹훅 미수신
-
진단:
- 대시보드 웹훅 전달 로그 확인
- 엔드포인트가 5초 이내 200 OK 반환하는지 체크
- curl 등으로 엔드포인트 수동 테스트
-
해결책:
- HTTPS + 유효한 SSL 사용
- Etsy 웹훅 IP 화이트리스트에 추가
- 서명 검증 로직 점검
- 개발 중 웹훅 테스트 도구 활용
이미지 업로드 실패
-
해결책:
- JPEG, PNG, GIF 형식만 허용
- 1장당 20MB 이하 확인
- base64 인코딩 올바른지 확인
- 상품 목록 생성 후 업로드
- 이미지 업로드는 반드시 순차 처리
프로덕션 배포 체크리스트
- [ ] 개발 모드 → 프로덕션 전환
- [ ] 리디렉션 URI를 실제 URL로 변경
- [ ] 토큰 안전 저장(암호화 DB 등)
- [ ] 자동 토큰 갱신 로직 적용
- [ ] 속도 제한/대기열 구현
- [ ] 웹훅 엔드포인트 HTTPS 적용
- [ ] 오류 처리/로깅 강화
- [ ] 할당량 모니터링
- [ ] 주요 장애 대응 런북 작성
- [ ] 여러 상점 계정 테스트
- [ ] 사용자 온보딩용 OAuth 설명서 제공
모니터링 및 경고
const metrics = {
apiCalls: { total: 0, successful: 0, failed: 0, rateLimited: 0 },
quotaUsage: { current: 0, limit: 10000, resetTime: null },
oauthTokens: { active: 0, expiring_soon: 0, refresh_failures: 0 },
webhooks: { received: 0, processed: 0, failed: 0 }
};
const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;
if (failureRate > 0.05) {
sendAlert('Etsy API failure rate above 5%');
}
if (metrics.quotaUsage.current < 500) {
sendAlert('Etsy API quota below 500 calls remaining');
}
실제 사용 사례
다중 채널 재고 동기화
- 과제: 수동 재고 입력 → 초과 판매 발생
- 해결: Etsy API 웹훅 + 중앙 재고 시스템
- 결과: 초과 판매 0건, 주 12시간 절약
흐름:
- 주문 생성 시 Etsy 웹훅 트리거
- 중앙 시스템에서 재고 차감
- Shopify/Amazon API로 수량 자동 업데이트
- 모든 내역 DB 기록
자동화 주문 처리
- 과제: 일 50건 주문 수동 처리 부담
- 해결: 주문 처리 업체 API와 Etsy API 통합
- 결과: 주문 → 생산까지 5분 내 자동 라우팅
통합:
- 웹훅으로
orders/create수신 - 주문 데이터 외부 API 전달
- 추적번호 생성 후 Etsy API로 업데이트
- 고객 자동 안내
분석 대시보드
- 과제: 여러 상점 통합 리포트 부재
- 해결: OAuth 기반 다중 상점 데이터 집계
- 결과: 판매, 트래픽, 전환 등 실시간 대시보드
수집 데이터:
- 상점 통계(상품, 판매, 수익)
- 주문/트렌드
- 상품별 성과
- 고객 리뷰
결론
- Etsy API는 인증, 상품/주문/재고/웹훅 등 대부분의 마켓플레이스 자동화를 지원합니다.
- OAuth 2.0 토큰 관리는 자동 갱신 및 안전 저장이 필수입니다.
- 속도 제한(초당 10회/시간당 1만회)은 대기열과 모니터링이 필요합니다.
- 웹훅으로 실시간 동기화, 재시도 및 오류처리로 안정적인 운영이 가능합니다.
- Apidog와 같은 도구를 활용해 API 통합 테스트와 협업을 효율적으로 진행하세요.
자주 묻는 질문
Etsy API는 무엇에 사용되나요?
Etsy API는 개발자가 Etsy와 통합된 앱을 만들 때 활용합니다. 대표적으로 다중 채널 재고 관리, 주문 자동화, 데이터 대시보드, 상품 관리 도구, CRM 시스템 구현에 쓰입니다.
Etsy API 키는 어떻게 얻나요?
Etsy 개발자 포털에서 계정 생성 후 내 앱 → 새 앱 생성 클릭. 등록 후 키 문자열(공개 식별자)과 공유 비밀(비공개 키) 발급. 환경 변수로 안전하게 보관하세요.
Etsy API는 무료인가요?
네, 무료입니다. 단, 초당 10회/시간당 1만회 호출 제한이 있습니다. 추가 할당량은 Etsy에 별도 신청해야 합니다.
어떤 인증 방식을 사용하나요?
Etsy는 OAuth 2.0 기반 인증을 사용합니다. 사용자가 앱에 권한 부여하면 액세스 토큰(1시간 유효)과 갱신 토큰을 받습니다.
속도 제한은 어떻게 처리하나요?
요청 대기열(rate limiter) 구현으로 초당 10회 이하 유지. x-etsy-quota-remaining 헤더로 시간당 잔여량 추적. 429 응답시 지수 백오프 재시도 적용.
실제 상점 없이 테스트 가능합니까?
네. 개발 모드 앱+테스트 상점 계정으로 실제 데이터와 무관하게 통합 테스트가 가능합니다.
웹훅 동작 방식은?
이벤트(신규 주문, 상품 수정 등) 발생 시 등록한 HTTPS 엔드포인트로 POST 전송. 서명 검증, 5초 이내 200 OK 반환 필수.
OAuth 토큰 만료시 처리법은?
1시간 만료 전 갱신 토큰으로 새 액세스 토큰을 받으세요. 자동 토큰 갱신 미들웨어 구현 권장.
상품 이미지도 API로 올릴 수 있나요?
네. 상품 생성 후 별도 API에서 base64 인코딩으로 업로드. 1장당 20MB, JPEG/PNG/GIF만 허용.
V2 → V3 마이그레이션 방법?
V3는 OAuth 2.0과 다른 엔드포인트 구조를 사용합니다. 인증 로직/엔드포인트를 /v3/application/으로 변경하고, 2026년 말까지 충분히 테스트 후 전환하세요.
Top comments (0)