Back to Browse

Etsy V3 Api Client MCP Server

Developer ToolsScan in ProgressMCP RegistryLocal
Free

Server data from the Official MCP Registry

A local read-only MCP server for Etsy shops, active listings, and listing variation inventory.

About

A local read-only MCP server for Etsy shops, active listings, and listing variation inventory.

Security Report

0.0
Use Caution0.0Moderate Risk

10 tools verified ยท Open access ยท No issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Remote servers are capped at 8.0 because source code is not available for review. The score reflects endpoint verification only.

How to Install

Add this to your MCP configuration file:

{
  "mcpServers": {
    "io-github-profplum700-etsy-mcp-server": {
      "args": [
        "-y",
        "@profplum700/etsy-mcp-server",
        "--yes"
      ],
      "command": "npx"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

Etsy API v3 Client

npm version License: MIT TypeScript

A modern, universal TypeScript/JavaScript client for the Etsy Open API v3 with full OAuth 2.0 PKCE authentication support. Works seamlessly in both browser and Node.js environments.

This repository is heavily based on the official Etsy OpenAPI specification (https://www.etsy.com/openapi/generated/oas/3.0.0.json), as referenced in Etsy's API documentation (https://developers.etsy.com/documentation/reference).

๐Ÿš€ Features

  • Universal Compatibility: Works in browsers, Node.js, and Web Workers
  • OAuth 2.0 PKCE Authentication: Full support for secure authentication flow
  • TypeScript First: Complete type definitions with IntelliSense support
  • Rate Limiting: Built-in request throttling to respect API limits
  • Token Management: Automatic token refresh with configurable storage
  • Caching: Optional response caching to improve performance
  • Error Handling: Comprehensive error types for different failure scenarios
  • Zero Dependencies: No external runtime dependencies

๐Ÿ“ฆ Installation

npm install @profplum700/etsy-v3-api-client
yarn add @profplum700/etsy-v3-api-client
pnpm add @profplum700/etsy-v3-api-client

๐Ÿค– Connect an agent to your Etsy shop

The companion @profplum700/etsy-mcp-server package runs locally over stdio and exposes read-only shop, active-listing, and listing-inventory tools. It requires Node.js 24+, your own approved Etsy developer app, and an OS credential store. It does not use a hosted endpoint.

Register http://localhost:3030/oauth/redirect as the Etsy app callback if required, then run this in a local interactive terminal:

npx --yes @profplum700/etsy-mcp-server@latest setup

Enter your keystring and shared secret in the hidden terminal prompts, authorize only shops_r and listings_r in Etsy, then choose whether to add the server to your user-level Codex MCP configuration. For another stdio client, configure npx --yes @profplum700/etsy-mcp-server@latest serve as its launch command.

You can connect multiple shops without replacing earlier profiles. Run setup --reuse-app --manual-browser to reuse the saved developer app credentials, then open the printed authorization URL in the browser profile signed in to the shop you want to add. Use status to see saved shops and use <shop name or ID> to choose which one MCP tools query. disconnect removes only the active shop; disconnect --all removes every saved profile. Use remove-codex to remove the Codex entry without disconnecting any shop.

Prompt for an agent: โ€œSet up the local Etsy MCP server. Do not ask me to paste Etsy credentials into chat or save them in a repository or environment file. Run the setup command in an interactive local terminal so I can enter them there; request only shops_r and listings_r, verify my shop, and offer the user-level Codex connection.โ€

๐Ÿ”ง Quick Start

Basic Setup

import { EtsyClient, AuthHelper } from '@profplum700/etsy-v3-api-client';

// Create authentication helper
const authHelper = new AuthHelper({
  keystring: 'your-api-key',
  redirectUri: 'https://your-app.com/callback',
  scopes: ['shops_r', 'listings_r']
});

// Get authorization URL
const authUrl = await authHelper.getAuthUrl();
console.log('Visit this URL to authorize:', authUrl);

// After user authorization, exchange code for tokens
const state = await authHelper.getState();
await authHelper.setAuthorizationCode('authorization-code-from-callback', state);
const tokens = await authHelper.getAccessToken();

// Create API client
const client = new EtsyClient({
  keystring: 'your-api-key',
  sharedSecret: 'your-shared-secret', // REQUIRED for v3 API compliance
  accessToken: tokens.access_token,
  refreshToken: tokens.refresh_token,
  expiresAt: tokens.expires_at
});

// Make API calls
const user = await client.getUser();
console.log('User:', user);

Browser Usage

<script type="module">
import { EtsyClient, AuthHelper } from 'https://unpkg.com/@profplum700/etsy-v3-api-client/dist/browser.esm.js';

// Your code here...
</script>

Or using UMD:

<script src="https://unpkg.com/@profplum700/etsy-v3-api-client/dist/browser.umd.js"></script>
<script>
const { EtsyClient, AuthHelper } = EtsyApiClient;
// Your code here...
</script>

Node.js Usage

// CommonJS
const { EtsyClient, AuthHelper } = require('@profplum700/etsy-v3-api-client');

// ES Modules
import { EtsyClient, AuthHelper } from '@profplum700/etsy-v3-api-client';

๐Ÿ” Authentication

OAuth 2.0 Flow

  1. Create AuthHelper with your app credentials
  2. Generate authorization URL for user to visit
  3. Handle callback with authorization code
  4. Exchange code for tokens
  5. Use tokens with EtsyClient
import { AuthHelper, ETSY_SCOPES } from '@profplum700/etsy-v3-api-client';

const authHelper = new AuthHelper({
  keystring: 'your-api-key',
  redirectUri: 'https://your-app.com/callback',
  scopes: [
    ETSY_SCOPES.SHOPS_READ,
    ETSY_SCOPES.LISTINGS_READ,
    ETSY_SCOPES.PROFILE_READ
  ]
});

// Step 1: Get authorization URL
const authUrl = await authHelper.getAuthUrl();
// Redirect user to authUrl

// Step 2: Handle callback (in your callback endpoint)
const { code, state } = getCallbackParams(); // Your implementation
const expectedState = await authHelper.getState();

if (state === expectedState) {
  await authHelper.setAuthorizationCode(code, state);
  const tokens = await authHelper.getAccessToken();
  // Store tokens securely
}

Available Scopes

import { ETSY_SCOPES, COMMON_SCOPE_COMBINATIONS } from '@profplum700/etsy-v3-api-client';

// Individual scopes
const scopes = [
  ETSY_SCOPES.SHOPS_READ,
  ETSY_SCOPES.LISTINGS_WRITE,
  ETSY_SCOPES.TRANSACTIONS_READ
];

// Pre-defined combinations
const readOnlyScopes = COMMON_SCOPE_COMBINATIONS.SHOP_READ_ONLY;
const managementScopes = COMMON_SCOPE_COMBINATIONS.SHOP_MANAGEMENT;

๐Ÿช Client Usage

Creating a Client

import { EtsyClient } from '@profplum700/etsy-v3-api-client';

const client = new EtsyClient({
  keystring: 'your-api-key',
  sharedSecret: 'your-shared-secret', // Get this from Your Apps page on Etsy
  accessToken: 'user-access-token',
  refreshToken: 'user-refresh-token',
  expiresAt: new Date('2024-12-31T23:59:59Z'),

  // Optional configuration
  rateLimiting: {
    enabled: true,
    maxRequestsPerSecond: 5,
    maxRequestsPerDay: 5000 // Conservative local fallback; Etsy response headers report this app's actual quota
  },
  caching: {
    enabled: true,
    ttl: 300 // 5 minutes
  }
});

Making API Calls

// Get current user
const user = await client.getUser();

// Get user's shops
const shops = await client.getUserShops();

// Get shop listings
const listings = await client.getListingsByShop('shop-id');

// Get specific listing
const listing = await client.getListing('listing-id');

// Search listings
const searchResults = await client.findAllListingsActive({
  keywords: 'vintage',
  taxonomy_id: 123,
  limit: 25
});

Error Handling

import { EtsyApiError, EtsyAuthError, EtsyRateLimitError } from '@profplum700/etsy-v3-api-client';

try {
  const user = await client.getUser();
} catch (error) {
  if (error instanceof EtsyAuthError) {
    // Handle authentication errors
    console.error('Auth error:', error.message, error.code);
  } else if (error instanceof EtsyRateLimitError) {
    // Handle rate limiting
    console.error('Rate limited. Retry after:', error.retryAfter);
  } else if (error instanceof EtsyApiError) {
    // Handle API errors
    console.error('API error:', error.statusCode, error.message);
  } else {
    // Handle other errors
    console.error('Unexpected error:', error);
  }
}

๐Ÿ’พ Token Storage

Built-in Storage Options

The client provides several storage mechanisms:

import { 
  createDefaultTokenStorage,
  LocalStorageTokenStorage,
  SessionStorageTokenStorage,
  FileTokenStorage,
  MemoryTokenStorage
} from '@profplum700/etsy-v3-api-client';

// Automatic storage selection based on environment
const storage = createDefaultTokenStorage();

// Browser localStorage
const localStorage = new LocalStorageTokenStorage('etsy-tokens');

// Browser sessionStorage  
const sessionStorage = new SessionStorageTokenStorage('etsy-tokens');

// Node.js file storage
const fileStorage = new FileTokenStorage('./tokens.json');

// In-memory storage (not persistent)
const memoryStorage = new MemoryTokenStorage();

// Use with client
const client = new EtsyClient(config, storage);

Custom Storage

import { TokenStorage } from '@profplum700/etsy-v3-api-client';

class CustomTokenStorage implements TokenStorage {
  async save(tokens: EtsyTokens): Promise<void> {
    // Your save implementation
  }

  async load(): Promise<EtsyTokens | null> {
    // Your load implementation
  }

  async clear(): Promise<void> {
    // Your clear implementation
  }
}

๐Ÿšฆ Rate Limiting

The client includes built-in rate limiting to respect Etsy's API limits:

const client = new EtsyClient({
  // ... other config
  rateLimiting: {
    enabled: true,
    maxRequestsPerSecond: 5,     // Conservative QPS fallback
    maxRequestsPerDay: 5000,     // Conservative rolling 24-hour fallback
    minRequestInterval: 200      // Minimum ms between requests (5 QPS fallback)
  }
});

// Check remaining requests in the rolling local window
const remaining = client.getRemainingRequests();
console.log(`${remaining} requests remaining in the rolling 24-hour window`);

When building a custom transport around the exported EtsyRateLimiter, keep the reservation ID attached to its response so concurrent, out-of-order headers cannot incorrectly restore exhausted quota:

const reservationId = await rateLimiter.waitForRateLimitWithReservation();
let response: Response;
try {
  response = await fetch(url);
} catch (error) {
  rateLimiter.releaseRequestSlot(reservationId);
  throw error;
}
rateLimiter.updateFromHeaders(response.headers, reservationId);

waitForRateLimit() remains available for source compatibility when you only need dispatch pacing. For custom transports that also feed response headers back to the limiter, use the reservation-aware method above; uncorrelated positive headers cannot safely reopen exhausted quota.

๐Ÿ”„ Caching

Optional response caching to improve performance:

const client = new EtsyClient({
  // ... other config
  caching: {
    enabled: true,
    ttl: 300 // Cache for 5 minutes
  }
});

// Clear cache when needed
await client.clearCache();

๐ŸŒ Environment Support

Browser

  • Modern browsers with ES2020+ support
  • Web Workers
  • Service Workers

Node.js

  • Node.js 20+
  • Full CommonJS and ES Module support

Package Exports

The package provides optimized builds for different environments:

{
  "imports": {
    "@profplum700/etsy-v3-api-client": "./dist/index.esm.js",
    "@profplum700/etsy-v3-api-client/browser": "./dist/browser.esm.js",
    "@profplum700/etsy-v3-api-client/node": "./dist/node.esm.js"
  }
}

๐Ÿ“š API Reference

EtsyClient Methods

User Methods
  • getUser() - Get current user info
  • getUserShops(userId?) - Get user's shops
Shop Methods
  • getShop(shopId) - Get shop details
  • getShopByOwnerUserId(userId) - Get shop by owner user ID
Personalization Methods (New)
  • getListingPersonalizations(listingId) - Get personalization questions for a listing
  • updateListingPersonalization(shopId, listingId, params) - Create or update personalization questions
  • deleteListingPersonalization(shopId, listingId) - Delete personalization
Listing Methods
  • getListing(listingId) - Get listing details
  • getListingsByShop(shopId, options?) - Get shop's listings
  • findAllListingsActive(options?) - Search active listings
Listing Management Endpoint Support
CategoryEndpointSupported
BuyerTaxonomygetBuyerTaxonomyNodesYes
BuyerTaxonomygetPropertiesByBuyerTaxonomyIdNo
SellerTaxonomygetSellerTaxonomyNodesYes
SellerTaxonomygetPropertiesByTaxonomyIdYes
ShopListingcreateDraftListingYes
ShopListinggetListingsByShopYes
ShopListingdeleteListingYes
ShopListinggetListingYes
ShopListingfindAllListingsActiveYes
ShopListingfindAllActiveListingsByShopNo
ShopListinggetListingsByListingIdsNo
ShopListinggetFeaturedListingsByShopNo
ShopListingdeleteListingPropertyNo
ShopListingupdateListingPropertyYes
ShopListinggetListingPropertyNo
ShopListinggetListingPropertiesYes
ShopListingupdateListingYes
ShopListinggetListingsByShopReceiptNo
ShopListinggetListingsByShopReturnPolicyNo
ShopListinggetListingsByShopSectionIdNo
ShopListinggetListingPersonalizationYes
ShopListingupdateListingPersonalizationYes
ShopListingdeleteListingPersonalizationYes
ShopListing FiledeleteListingFileNo
ShopListing FilegetListingFileNo
ShopListing FilegetAllListingFilesNo
ShopListing FileuploadListingFileNo
ShopListing ImagedeleteListingImageYes
ShopListing ImagegetListingImageYes
ShopListing ImagegetListingImagesYes
ShopListing ImageuploadListingImageYes
ShopListing InventorygetListingInventoryYes
ShopListing InventoryupdateListingInventoryYes
ShopListing OfferinggetListingOfferingNo
ShopListing ProductgetListingProductNo
ShopListing TranslationcreateListingTranslationNo
ShopListing TranslationgetListingTranslationNo
ShopListing TranslationupdateListingTranslationNo
ShopListing VariationImagegetListingVariationImagesNo
ShopListing VariationImageupdateVariationImagesNo
ShopListing VideodeleteListingVideoNo
ShopListing VideogetListingVideoNo
ShopListing VideogetListingVideosNo
ShopListing VideouploadListingVideoNo
OtherpingNo
OthertokenScopesNo
Ledger EntrygetShopPaymentAccountLedgerEntryYes
Ledger EntrygetShopPaymentAccountLedgerEntriesYes
PaymentgetShopPaymentByReceiptIdNo
PaymentgetPaymentAccountLedgerEntryNo
PaymentgetPaymentsNo
Shop ReceiptgetShopReceiptYes
Shop ReceiptupdateShopReceiptYes
Shop ReceiptgetShopReceiptsYes
Shop ReceiptsearchAllShopReceiptsNo
Shop Receipt TransactionsgetShopReceiptTransactionYes
Shop Receipt TransactionsgetShopReceiptTransactionsByListingNo
Shop Receipt TransactionsgetShopReceiptTransactionsByReceiptYes
Shop Receipt TransactionsgetShopReceiptTransactionsByShopNo
ReviewgetReviewsByListingYes
ReviewgetReviewsByShopYes
Shop HolidayPreferencesgetShopHolidayPreferencesNo
Shop HolidayPreferencesupdateShopHolidayPreferencesNo
Shop ProcessingProfilescreateShopShippingProfileUpgradeNo
Shop ProcessingProfilesgetShopShippingProfileUpgradesYes
Shop ProcessingProfilesdeleteShopShippingProfileUpgradeNo
Shop ProcessingProfilesgetShopShippingProfileUpgradeNo
Shop ProcessingProfilesupdateShopShippingProfileUpgradeNo
Shop ShippingProfilegetShippingCarriersNo
Shop ShippingProfilecreateShopShippingProfileYes
Shop ShippingProfilegetShopShippingProfilesYes
Shop ShippingProfiledeleteShopShippingProfileYes
Shop ShippingProfilegetShopShippingProfileYes
Shop ShippingProfileupdateShopShippingProfileYes
Shop ShippingProfilecreateShopShippingProfileDestinationYes
Shop ShippingProfilegetShopShippingProfileDestinationsByShippingProfileYes
Shop ShippingProfiledeleteShopShippingProfileDestinationYes
Shop ShippingProfileupdateShopShippingProfileDestinationYes
Shop ShippingProfilecreateShopShippingProfileUpgradeNo
Shop ShippingProfilegetShopShippingProfileUpgradesYes
Shop ShippingProfiledeleteShopShippingProfileUpgradeNo
Shop ShippingProfileupdateShopShippingProfileUpgradeNo
ShopgetShopYes
ShopupdateShopYes
ShopgetShopByOwnerUserIdYes
ShopfindShopsNo
Shop ProductionPartnergetShopProductionPartnersYes
Shop SectioncreateShopSectionYes
Shop SectiongetShopSectionsYes
Shop SectiondeleteShopSectionYes
Shop SectiongetShopSectionYes
Shop SectionupdateShopSectionYes
Shop Return PolicyconsolidateShopReturnPoliciesNo
Shop Return PolicycreateShopReturnPolicyNo
Shop Return PolicygetShopReturnPoliciesNo
Shop Return PolicydeleteShopReturnPolicyNo
Shop Return PolicygetShopReturnPolicyNo
Shop Return PolicyupdateShopReturnPolicyNo
UsergetUserYes
UsergetMeNo
UserAddressdeleteUserAddressNo
UserAddressgetUserAddressNo
UserAddressgetUserAddressesNo
Review Methods
  • getReviewsByListing(listingId, options?) - Get reviews for a listing
  • getReviewsByShop(shopId, options?) - Get reviews for a shop
Search Methods
  • findAllListingsActive(params) - Search listings with filters

AuthHelper Methods

  • getAuthUrl() - Generate authorization URL
  • setAuthorizationCode(code, state) - Set auth code from callback
  • getAccessToken() - Exchange code for tokens
  • getState() - Get current state parameter
  • getCodeVerifier() - Get PKCE code verifier

TokenManager Methods

  • getAccessToken() - Get current access token (refreshes if needed)
  • refreshToken() - Manually refresh tokens
  • getCurrentTokens() - Get current token set
  • isTokenExpired() - Check if token is expired
  • clearTokens() - Clear stored tokens

๐Ÿ› ๏ธ Configuration Options

interface EtsyClientConfig {
  keystring: string;                    // Required: Your API key
  sharedSecret: string;                 // Required: Your shared secret (from Your Apps page)
  accessToken?: string;                 // User's access token
  refreshToken?: string;                // User's refresh token
  expiresAt?: Date;                     // Token expiration date
  refreshSave?: (token, refresh, expires) => void; // Token save callback

  rateLimiting?: {
    enabled?: boolean;                  // Enable rate limiting
    maxRequestsPerSecond?: number;      // Requests per second limit
    maxRequestsPerDay?: number;         // Daily request limit  
    minRequestInterval?: number;        // Minimum interval between requests (ms)
  };
  
  caching?: {
    enabled?: boolean;                  // Enable response caching
    ttl?: number;                       // Cache TTL in seconds
  };
}

๐Ÿ“ Examples

Complete Authentication Flow

import { AuthHelper, EtsyClient, createDefaultTokenStorage } from '@profplum700/etsy-v3-api-client';

async function authenticateAndFetchData() {
  // Step 1: Initialize authentication
  const authHelper = new AuthHelper({
    keystring: process.env.ETSY_API_KEY!,
    redirectUri: 'http://localhost:3000/callback',
    scopes: ['shops_r', 'listings_r', 'profile_r']
  });

  // Step 2: Get authorization URL
  const authUrl = await authHelper.getAuthUrl();
  console.log('Visit:', authUrl);

  // Step 3: Handle callback (pseudo-code)
  const { code, state } = await waitForCallback();
  const expectedState = await authHelper.getState();
  
  if (state !== expectedState) {
    throw new Error('State mismatch');
  }

  // Step 4: Exchange for tokens
  await authHelper.setAuthorizationCode(code, state);
  const tokens = await authHelper.getAccessToken();

  // Step 5: Create client with storage
  const storage = createDefaultTokenStorage();
  const client = new EtsyClient({
    keystring: process.env.ETSY_API_KEY!,
    ...tokens
  }, storage);

  // Step 6: Use the API
  const user = await client.getUser();
  const shops = await client.getUserShops();
  
  if (shops.length > 0) {
    const listings = await client.getListingsByShop(shops[0].shop_id.toString());
    console.log(`Found ${listings.length} listings`);
  }
}

Simple Browser Example

Here's a complete working example for browser environments:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Etsy API Client - Browser Example</title>
</head>
<body>
    <div id="app">
        <h1>Etsy API Browser Example</h1>
        <button id="loginBtn">Login with Etsy</button>
        <div id="userInfo" style="display: none;">
            <h2>User Information</h2>
            <div id="userData"></div>
            <button id="getShopsBtn">Get My Shops</button>
            <div id="shopsData"></div>
        </div>
    </div>

    <script type="module">
        import { AuthHelper, EtsyClient, ETSY_SCOPES } from 'https://unpkg.com/@profplum700/etsy-v3-api-client@1.0.0/dist/browser.esm.js';

        // Replace with your actual API key
        const API_KEY = 'your-api-key-here';
        const REDIRECT_URI = window.location.origin + window.location.pathname;

        let authHelper;
        let client;

        // Initialize auth helper
        function initAuth() {
            authHelper = new AuthHelper({
                keystring: API_KEY,
                redirectUri: REDIRECT_URI,
                scopes: [
                    ETSY_SCOPES.SHOPS_READ,
                    ETSY_SCOPES.LISTINGS_READ,
                    ETSY_SCOPES.PROFILE_READ
                ]
            });
        }

        // Handle login button click
        document.getElementById('loginBtn').addEventListener('click', async () => {
            const authUrl = await authHelper.getAuthUrl();
            window.location.href = authUrl;
        });

        // Handle OAuth callback
        async function handleCallback() {
            const urlParams = new URLSearchParams(window.location.search);
            const code = urlParams.get('code');
            const state = urlParams.get('state');

            if (code && state) {
                try {
                    const expectedState = await authHelper.getState();
                    
                    if (state === expectedState) {
                        await authHelper.setAuthorizationCode(code, state);
                        const tokens = await authHelper.getAccessToken();
                        
                        // Create client
                        client = new EtsyClient({
                            keystring: API_KEY,
                            accessToken: tokens.access_token,
                            refreshToken: tokens.refresh_token,
                            expiresAt: tokens.expires_at,
                            rateLimiting: { enabled: true },
                            caching: { enabled: true, ttl: 300 }
                        });

                        // Store tokens in localStorage for persistence
                        localStorage.setItem('etsy_tokens', JSON.stringify(tokens));
                        
                        // Clear URL parameters
                        window.history.replaceState({}, document.title, window.location.pathname);
                        
                        // Show user info
                        await showUserInfo();
                    } else {
                        console.error('State mismatch');
                    }
                } catch (error) {
                    console.error('Authentication failed:', error);
                    alert('Authentication failed. Please try again.');
                }
            }
        }

        // Show user information
        async function showUserInfo() {
            try {
                const user = await client.getUser();
                
                document.getElementById('loginBtn').style.display = 'none';
                document.getElementById('userInfo').style.display = 'block';
                document.getElementById('userData').innerHTML = `
                    <p><strong>User ID:</strong> ${user.user_id}</p>
                    <p><strong>Login Name:</strong> ${user.login_name || 'N/A'}</p>
                    <p><strong>Primary Email:</strong> ${user.primary_email || 'N/A'}</p>
                `;
            } catch (error) {
                console.error('Failed to get user info:', error);
                alert('Failed to get user information.');
            }
        }

        // Get user shops
        document.getElementById('getShopsBtn').addEventListener('click', async () => {
            try {
                const shops = await client.getUserShops();
                
                if (shops.length > 0) {
                    const shopsHtml = shops.map(shop => `
                        <div style="border: 1px solid #ccc; padding: 10px; margin: 10px 0;">
                            <h3>${shop.shop_name}</h3>
                            <p><strong>Shop ID:</strong> ${shop.shop_id}</p>
                            <p><strong>Title:</strong> ${shop.title || 'N/A'}</p>
                            <p><strong>URL:</strong> <a href="${shop.url}" target="_blank">${shop.url}</a></p>
                        </div>
                    `).join('');
                    
                    document.getElementById('shopsData').innerHTML = `
                        <h3>Your Shops (${shops.length})</h3>
                        ${shopsHtml}
                    `;
                } else {
                    document.getElementById('shopsData').innerHTML = '<p>No shops found.</p>';
                }
            } catch (error) {
                console.error('Failed to get shops:', error);
                alert('Failed to get shop information.');
            }
        });

        // Check for existing tokens on page load
        async function checkExistingAuth() {
            const storedTokens = localStorage.getItem('etsy_tokens');
            
            if (storedTokens) {
                try {
                    const tokens = JSON.parse(storedTokens);
                    
                    // Check if tokens are still valid
                    if (new Date(tokens.expires_at) > new Date()) {
                        client = new EtsyClient({
                            keystring: API_KEY,
                            ...tokens,
                            rateLimiting: { enabled: true },
                            caching: { enabled: true, ttl: 300 }
                        });
                        
                        await showUserInfo();
                    } else {
                        // Tokens expired, clear them
                        localStorage.removeItem('etsy_tokens');
                    }
                } catch (error) {
                    console.error('Failed to restore session:', error);
                    localStorage.removeItem('etsy_tokens');
                }
            }
        }

        // Initialize the application
        initAuth();
        await handleCallback();
        await checkExistingAuth();
    </script>
</body>
</html>

This example demonstrates:

  • Complete OAuth 2.0 flow in the browser
  • Token persistence using localStorage
  • Error handling for authentication failures
  • Making API calls to get user and shop information
  • Automatic session restoration on page reload

React Hook Example

import { useState, useEffect } from 'react';
import { EtsyClient, createDefaultTokenStorage } from '@profplum700/etsy-v3-api-client';

function useEtsyClient(tokens) {
  const [client, setClient] = useState(null);

  useEffect(() => {
    if (tokens) {
      const storage = createDefaultTokenStorage({ preferSession: true });
      const etsyClient = new EtsyClient({
        keystring: process.env.REACT_APP_ETSY_API_KEY,
        ...tokens,
        rateLimiting: { enabled: true },
        caching: { enabled: true, ttl: 300 }
      }, storage);
      
      setClient(etsyClient);
    }
  }, [tokens]);

  return client;
}

๐Ÿ“š Documentation

Comprehensive documentation and examples are available in the docs/ directory:

Guides

Troubleshooting

Example Applications

Complete, working example applications demonstrating various use cases:

๐Ÿค Contributing

Contributions are welcome! Please read our Contributing Guidelines for details.

  1. Fork the repository
  2. Create your feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ”— Links

โ“ Support

If you have questions or need help:

  1. Check the API Documentation
  2. Search GitHub Issues
  3. Create a new issue if your question isn't answered

๐Ÿท๏ธ Changelog

See CHANGELOG.md for version history and release notes.

Reviews

No reviews yet

Be the first to review this server!