MacroEstimator

Code Examples

Ready-to-use code examples for common use cases in curl, Python, and JavaScript.

curl Examples

Basic text estimation

curl -X POST https://api.macroestimator.com/v1/estimate/sync \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "content": "2 eggs, 2 slices of bacon, and a slice of toast with butter"
      }
    ]
  }'

Upload and estimate from image

# First, upload the image
IMAGE_URL=$(curl -s -X POST https://api.macroestimator.com/v1/upload/base64 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    "image": "data:image/jpeg;base64,$(base64 -i meal.jpg)"
  }" | jq -r '.url')

# Then estimate from the image
curl -X POST https://api.macroestimator.com/v1/estimate/sync \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "image_url",
            "image_url": {
              "url": "$IMAGE_URL"
            }
          },
          {
            "type": "text",
            "text": "What are the macros?"
          }
        ]
      }
    ]
  }"

Search foods database

curl "https://api.macroestimator.com/v1/foods?q=grilled%20salmon&limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"

Create a custom recipe

curl -X POST https://api.macroestimator.com/v1/recipes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Post-Workout Shake",
    "calories": 450,
    "protein": 40,
    "carbs": 55,
    "fat": 8,
    "serving_description": "1 shake (24oz)",
    "aliases": ["protein shake", "workout shake", "PWO shake"]
  }'

Python Examples

Basic estimation with error handling

import requests
from typing import Optional

class MacroEstimator:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.macroestimator.com"

    def estimate(self, description: str) -> Optional[dict]:
        """Estimate nutrition from a text description."""
        try:
            response = requests.post(
                f"{self.base_url}/v1/estimate/sync",
                headers={
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                },
                json={
                    "messages": [
                        {"role": "user", "content": description}
                    ]
                },
                timeout=30
            )
            response.raise_for_status()
            return response.json()
        except requests.RequestException as e:
            print(f"Error: {e}")
            return None

# Usage
client = MacroEstimator("YOUR_API_KEY")
result = client.estimate("chicken caesar salad with croutons")

if result:
    print(f"Total calories: {result['totals']['calories']}")
    print(f"Protein: {result['totals']['protein']}g")
    print(f"Carbs: {result['totals']['carbs']}g")
    print(f"Fat: {result['totals']['fat']}g")

    print("\nBreakdown:")
    for item in result["items"]:
        print(f"  - {item['name']}: {item['calories']} cal")

Image upload and estimation

import requests
import base64
from pathlib import Path

class MacroEstimator:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.macroestimator.com"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }

    def upload_image(self, image_path: str) -> str:
        """Upload an image and return the URL."""
        image_data = Path(image_path).read_bytes()
        b64_data = base64.b64encode(image_data).decode()

        # Detect content type
        suffix = Path(image_path).suffix.lower()
        content_type = {
            ".jpg": "image/jpeg",
            ".jpeg": "image/jpeg",
            ".png": "image/png",
            ".webp": "image/webp"
        }.get(suffix, "image/jpeg")

        response = requests.post(
            f"{self.base_url}/v1/upload/base64",
            headers=self.headers,
            json={"image": f"data:{content_type};base64,{b64_data}"}
        )
        response.raise_for_status()
        return response.json()["url"]

    def estimate_from_image(self, image_path: str, prompt: str = "What are the macros?") -> dict:
        """Upload an image and get nutrition estimate."""
        image_url = self.upload_image(image_path)

        response = requests.post(
            f"{self.base_url}/v1/estimate/sync",
            headers=self.headers,
            json={
                "messages": [
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "image_url",
                                "image_url": {"url": image_url}
                            },
                            {"type": "text", "text": prompt}
                        ]
                    }
                ]
            }
        )
        response.raise_for_status()
        return response.json()

# Usage
client = MacroEstimator("YOUR_API_KEY")
result = client.estimate_from_image("lunch.jpg", "Estimate the macros for this meal")
print(f"Total: {result['totals']['calories']} calories")

Managing recipes

import requests

class RecipeManager:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.macroestimator.com/v1/recipes"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }

    def list_recipes(self) -> list:
        """Get all saved recipes."""
        response = requests.get(self.base_url, headers=self.headers)
        response.raise_for_status()
        return response.json()["recipes"]

    def create_recipe(self, name: str, calories: int, protein: int,
                     carbs: int, fat: int, **kwargs) -> dict:
        """Create a new recipe."""
        data = {
            "name": name,
            "calories": calories,
            "protein": protein,
            "carbs": carbs,
            "fat": fat,
            **kwargs
        }
        response = requests.post(self.base_url, headers=self.headers, json=data)
        response.raise_for_status()
        return response.json()

    def delete_recipe(self, recipe_id: str) -> bool:
        """Delete a recipe."""
        response = requests.delete(
            f"{self.base_url}/{recipe_id}",
            headers=self.headers
        )
        return response.status_code == 200

# Usage
recipes = RecipeManager("YOUR_API_KEY")

# Create a recipe
new_recipe = recipes.create_recipe(
    name="Homemade Granola",
    calories=280,
    protein=6,
    carbs=42,
    fat=10,
    serving_description="1/2 cup",
    aliases=["granola", "my granola"]
)
print(f"Created: {new_recipe['id']}")

# List all recipes
for recipe in recipes.list_recipes():
    print(f"- {recipe['name']}: {recipe['calories']} cal")

JavaScript Examples

Basic estimation with TypeScript types

interface EstimateItem {
  name: string;
  quantity: number;
  unit: string;
  calories: number;
  protein: number;
  carbs: number;
  fat: number;
}

interface EstimateResponse {
  items: EstimateItem[];
  totals: {
    calories: number;
    protein: number;
    carbs: number;
    fat: number;
  };
}

class MacroEstimator {
  private apiKey: string;
  private baseUrl = "https://api.macroestimator.com";

  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }

  async estimate(description: string): Promise<EstimateResponse> {
    const response = await fetch(`${this.baseUrl}/v1/estimate/sync`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${this.apiKey}`,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        messages: [{ role: "user", content: description }]
      })
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }

    return response.json();
  }
}

// Usage
const client = new MacroEstimator("YOUR_API_KEY");
const result = await client.estimate("grilled cheese sandwich with tomato soup");

console.log(`Total: ${result.totals.calories} calories`);
result.items.forEach(item => {
  console.log(`  ${item.name}: ${item.calories} cal`);
});

Streaming response handling

async function* streamEstimate(
  apiKey: string,
  description: string
): AsyncGenerator<string> {
  const response = await fetch(
    "https://api.macroestimator.com/v1/estimate",
    {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${apiKey}`,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        messages: [{ role: "user", content: description }]
      })
    }
  );

  if (!response.body) {
    throw new Error("No response body");
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    yield chunk;
  }
}

// Usage
async function main() {
  for await (const chunk of streamEstimate("YOUR_API_KEY", "spaghetti and meatballs")) {
    process.stdout.write(chunk);
  }
}

main();

React hook for estimation

import { useState, useCallback } from "react";

interface UseEstimateResult {
  estimate: (description: string) => Promise<void>;
  result: EstimateResponse | null;
  loading: boolean;
  error: string | null;
}

export function useEstimate(apiKey: string): UseEstimateResult {
  const [result, setResult] = useState<EstimateResponse | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const estimate = useCallback(async (description: string) => {
    setLoading(true);
    setError(null);

    try {
      const response = await fetch(
        "https://api.macroestimator.com/v1/estimate/sync",
        {
          method: "POST",
          headers: {
            "Authorization": `Bearer ${apiKey}`,
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            messages: [{ role: "user", content: description }]
          })
        }
      );

      if (!response.ok) {
        throw new Error(`API error: ${response.status}`);
      }

      const data = await response.json();
      setResult(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Unknown error");
    } finally {
      setLoading(false);
    }
  }, [apiKey]);

  return { estimate, result, loading, error };
}

// Usage in a component
function NutritionEstimator() {
  const { estimate, result, loading, error } = useEstimate("YOUR_API_KEY");
  const [input, setInput] = useState("");

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    estimate(input);
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Describe your meal..."
        />
        <button type="submit" disabled={loading}>
          {loading ? "Estimating..." : "Estimate"}
        </button>
      </form>

      {error && <p className="error">{error}</p>}

      {result && (
        <div>
          <h3>Total: {result.totals.calories} calories</h3>
          <ul>
            {result.items.map((item, i) => (
              <li key={i}>
                {item.name}: {item.calories} cal,
                {item.protein}g protein,
                {item.carbs}g carbs,
                {item.fat}g fat
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

Error Handling

Here is an example of comprehensive error handling:

import requests
from requests.exceptions import RequestException, Timeout, HTTPError

def safe_estimate(api_key: str, description: str) -> dict:
    """Make an API request with comprehensive error handling."""
    try:
        response = requests.post(
            "https://api.macroestimator.com/v1/estimate/sync",
            headers={
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json"
            },
            json={"messages": [{"role": "user", "content": description}]},
            timeout=30
        )

        # Check rate limits
        remaining = response.headers.get("X-RateLimit-Remaining")
        if remaining and int(remaining) < 10:
            print(f"Warning: Only {remaining} requests remaining")

        response.raise_for_status()
        return response.json()

    except Timeout:
        print("Request timed out. Please try again.")
        return {"error": "timeout"}

    except HTTPError as e:
        if e.response.status_code == 401:
            print("Invalid API key")
        elif e.response.status_code == 429:
            retry_after = e.response.headers.get("Retry-After", "60")
            print(f"Rate limited. Retry after {retry_after} seconds")
        elif e.response.status_code >= 500:
            print("Server error. Please try again later.")
        else:
            print(f"HTTP error: {e.response.status_code}")
        return {"error": str(e)}

    except RequestException as e:
        print(f"Network error: {e}")
        return {"error": "network"}

# Usage
result = safe_estimate("YOUR_API_KEY", "turkey sandwich")
if "error" not in result:
    print(f"Calories: {result['totals']['calories']}")