Academy / PrintPal API and Automation / Error Handling and Best Practices

Error Handling and Best Practices

10 min read

When you're writing scripts or building applications that call an external API, things will occasionally go wrong. Images might be invalid, credits might run out, the network might hiccup, or rate limits might kick in. Good error handling is the difference between a script that crashes mysteriously and one that tells you exactly what went wrong and recovers gracefully.

The PrintPal client libraries provide specific exception classes for every type of error the API can return. Let's go through each one.

The Error Types

Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from printpal import (
    PrintPal,
    PrintPalError,          # Base error - catches everything
    AuthenticationError,     # Invalid or missing API key
    InsufficientCreditsError,# Not enough credits
    ValidationError,         # Invalid parameters
    GenerationError,         # Generation failed
    NotFoundError,           # Resource not found
    RateLimitError,          # Rate limit exceeded
    TimeoutError,            # Request or poll timed out
)

JavaScript/TypeScript:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import {
    PrintPal,
    PrintPalError,          // Base error
    AuthenticationError,     // Invalid API key
    InsufficientCreditsError,// Not enough credits
    ValidationError,         // Invalid parameters
    GenerationError,         // Generation failed
    NotFoundError,           // Resource not found
    RateLimitError,          // Too many requests
    TimeoutError,            // Timed out
} from 'printpal';

Writing a Robust Generation Function

Here's how to handle errors properly in a real-world scenario:

Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from printpal import (
    PrintPal, Quality, Format,
    AuthenticationError,
    InsufficientCreditsError,
    ValidationError,
    RateLimitError,
    GenerationError,
    TimeoutError,
    PrintPalError,
)
import time

def generate_model(image_path, output_path, quality=Quality.DEFAULT):
    """Generate a 3D model with full error handling."""
    client = PrintPal()

    try:
        # Check credits before starting
        credits = client.get_credits()
        print(f"Credits available: {credits.credits}")

        # Generate and download
        path = client.generate_and_download(
            image_path=image_path,
            output_path=output_path,
            quality=quality,
        )
        print(f"Success! Model saved to: {path}")
        return path

    except AuthenticationError:
        print("Error: Invalid API key. Check your PRINTPAL_API_KEY.")
        return None

    except InsufficientCreditsError as e:
        print(f"Error: Not enough credits.")
        print(f"  Need: {e.credits_required}, Have: {e.credits_available}")
        print(f"  Purchase more at: https://printpal.io/buy-credits")
        return None

    except ValidationError as e:
        print(f"Error: Invalid parameters - {e.message}")
        return None

    except RateLimitError as e:
        print(f"Rate limited. Waiting {e.retry_after} seconds...")
        time.sleep(int(e.retry_after or 60))
        # You could retry here
        return None

    except GenerationError as e:
        print(f"Generation failed: {e.message}")
        print(f"  Generation UID: {e.generation_uid}")
        return None

    except TimeoutError:
        print("Generation timed out. Try again or use a lower quality.")
        return None

    except PrintPalError as e:
        print(f"API error: {e.message}")
        return None

JavaScript/TypeScript:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import {
    PrintPal, Quality, Format,
    AuthenticationError,
    InsufficientCreditsError,
    ValidationError,
    RateLimitError,
    GenerationError,
    TimeoutError,
    PrintPalError,
} from 'printpal';

async function generateModel(imagePath, outputPath, quality = Quality.DEFAULT) {
    const client = new PrintPal({ apiKey: process.env.PRINTPAL_API_KEY });

    try {
        const credits = await client.getCredits();
        console.log(`Credits available: ${credits.credits}`);

        const path = await client.generateAndDownload(
            imagePath,
            outputPath,
            { quality }
        );
        console.log(`Success! Model saved to: ${path}`);
        return path;

    } catch (error) {
        if (error instanceof AuthenticationError) {
            console.error('Invalid API key. Check your PRINTPAL_API_KEY.');
        } else if (error instanceof InsufficientCreditsError) {
            console.error(`Not enough credits. Need: ${error.creditsRequired}`);
        } else if (error instanceof RateLimitError) {
            console.error('Rate limited. Try again later.');
        } else if (error instanceof GenerationError) {
            console.error(`Generation failed: ${error.message}`);
        } else if (error instanceof TimeoutError) {
            console.error('Generation timed out.');
        } else if (error instanceof PrintPalError) {
            console.error(`API error: ${error.message}`);
        } else {
            throw error;
        }
        return null;
    }
}
What error type is raised when your account doesn't have enough credits for a generation?
AuthenticationError
InsufficientCreditsError
ValidationError
GenerationError

Best Practice: Check Credits Before Generating

A simple but valuable pattern is to check your credit balance before submitting a generation. This avoids wasting time on a request that will fail:

Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from printpal import CREDIT_COSTS, Quality

# Know the cost before you spend
quality = Quality.SUPER
cost = CREDIT_COSTS[quality]

credits = client.get_credits()
if credits.credits < cost:
    print(f"Not enough credits. Need {cost}, have {credits.credits}.")
else:
    # Safe to proceed
    result = client.generate_from_image("image.png", quality=quality)

Best Practice: Use Environment Variables for Configuration

Keep your API key and other configuration out of your source code:

Python:

1
2
3
4
5
6
7
import os

API_KEY = os.environ.get("PRINTPAL_API_KEY")
if not API_KEY:
    raise RuntimeError("Please set the PRINTPAL_API_KEY environment variable")

client = PrintPal(api_key=API_KEY)

For projects with multiple settings, consider using a .env file with the python-dotenv library (just make sure .env is in your .gitignore).

Best Practice: Use the Context Manager (Python)

The Python client supports context managers, which automatically clean up the HTTP session when you're done:

1
2
3
4
5
6
from printpal import PrintPal

with PrintPal() as client:
    credits = client.get_credits()
    print(f"Credits: {credits.credits}")
    # Session is automatically closed when the block exits

This is especially good practice in scripts that run and exit, or in web applications where you want to avoid connection leaks.

Best Practice: Monitor Your Usage

The API provides a usage endpoint that shows your recent activity:

Python:

1
2
3
4
5
6
7
8
9
usage = client.get_usage()
print(f"API key: {usage.api_key.name}")
print(f"Total requests: {usage.api_key.total_requests}")
print(f"Credits used: {usage.api_key.credits_used}")
print(f"Credits remaining: {usage.credits_remaining}")

# Show recent requests
for req in usage.recent_requests[:5]:
    print(f"  {req['endpoint']} - {req['status_code']} - {req['credits_used']} credits")

This is useful for debugging, auditing, and keeping track of how your automated processes are consuming credits.

Why is using a context manager (the 'with' statement) recommended for the Python client?
It makes the generation faster
It automatically retries failed requests
It automatically closes the HTTP session when done, preventing connection leaks
It provides better error messages
Have you finished this lesson?