> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avert.ldeo.columbia.edu/llms.txt
> Use this file to discover all available pages before exploring further.

# Migration Guide: v1 to v2

> Step-by-step guide to migrating from AVERT Imagery API v1 to v2

## Overview

This guide helps you migrate from the legacy AVERT Imagery API (v1) to the new unified API v2. The migration involves updating endpoints, parameter names, and response handling.

<Warning>
  **API v1 will continue to work** but is no longer actively maintained. We strongly recommend migrating to v2 for better features and ongoing support.
</Warning>

## Why Migrate?

<CardGroup cols={2}>
  <Card title="Unified API" icon="merge">
    Single endpoint replaces multiple endpoints
  </Card>

  <Card title="Better Filtering" icon="filter">
    New atmospheric and temporal filters
  </Card>

  <Card title="Smart Downloads" icon="download">
    Get estimates before downloading
  </Card>

  <Card title="Improved Pagination" icon="book">
    Comprehensive pagination metadata
  </Card>
</CardGroup>

## Breaking Changes Summary

| Change             | v1                                                              | v2                                                           |
| ------------------ | --------------------------------------------------------------- | ------------------------------------------------------------ |
| **Endpoint**       | `/api/imagery/infrared/query`<br />`/api/imagery/visible/query` | `/api/imagery/q`                                             |
| **Image type**     | URL path                                                        | `?imageType=infrared\|visible`                               |
| **Date params**    | `search_from`, `search_to`                                      | `datefrom`, `dateto`                                         |
| **Date format**    | ISO 8601 (`2022-10-01T00:00`)                                   | Compact (`yyyymmddhhmmss`)                                   |
| **Response**       | Array `[{...}, {...}]`                                          | Object with pagination `{results: [...], pagination: {...}}` |
| **Download limit** | Fixed 100 images                                                | Configurable up to 200                                       |

## Migration Steps

### Step 1: Update Endpoint URLs

**v1: Separate endpoints**

```bash theme={null}
# Infrared images
https://avert-legacy.ldeo.columbia.edu/api/imagery/infrared/query

# Visible images
https://avert-legacy.ldeo.columbia.edu/api/imagery/visible/query
```

**v2: Single unified endpoint**

```bash theme={null}
# Infrared images
https://avert.ldeo.columbia.edu/api/imagery/q?imageType=infrared

# Visible images
https://avert.ldeo.columbia.edu/api/imagery/q?imageType=visible
```

<CodeGroup>
  ```python Python - Before (v1) theme={null}
  # Separate endpoints for each image type
  infrared_url = "https://avert-legacy.ldeo.columbia.edu/api/imagery/infrared/query"
  visible_url = "https://avert-legacy.ldeo.columbia.edu/api/imagery/visible/query"

  infrared_images = requests.get(infrared_url, params=params).json()
  visible_images = requests.get(visible_url, params=params).json()
  ```

  ```python Python - After (v2) theme={null}
  # Single endpoint with imageType parameter
  base_url = "https://avert.ldeo.columbia.edu/api/imagery/q"

  infrared_images = requests.get(base_url, params={**params, 'imageType': 'infrared'}).json()
  visible_images = requests.get(base_url, params={**params, 'imageType': 'visible'}).json()
  ```
</CodeGroup>

### Step 2: Update Parameter Names

**Date range parameters have new names and format:**

| v1 Parameter  | v2 Parameter | Format Change               |
| ------------- | ------------ | --------------------------- |
| `search_from` | `datefrom`   | ISO 8601 → `yyyymmddhhmmss` |
| `search_to`   | `dateto`     | ISO 8601 → `yyyymmddhhmmss` |

<CodeGroup>
  ```python Python - Before (v1) theme={null}
  params = {
      'site': 'CLNE',
      'vnum': 311240,
      'search_from': '2022-10-01T00:00',
      'search_to': '2022-10-02T00:00'
  }
  ```

  ```python Python - After (v2) theme={null}
  params = {
      'site': 'CLNE',
      'vnum': 311240,
      'datefrom': '20221001000000',
      'dateto': '20221002000000'
  }
  ```
</CodeGroup>

**Helper function for date conversion:**

<CodeGroup>
  ```python Python theme={null}
  from datetime import datetime

  def convert_to_v2_date(iso_date_str):
      """Convert ISO 8601 date to v2 format"""
      # Parse ISO 8601 format (e.g., "2022-10-01T00:00")
      dt = datetime.fromisoformat(iso_date_str)
      # Return v2 format (e.g., "20221001000000")
      return dt.strftime('%Y%m%d%H%M%S')

  # Usage
  v1_date = '2022-10-01T00:00'
  v2_date = convert_to_v2_date(v1_date)
  print(v2_date)  # '20221001000000'
  ```

  ```javascript JavaScript theme={null}
  function convertToV2Date(isoDateStr) {
    // Parse ISO 8601 format (e.g., "2022-10-01T00:00")
    const date = new Date(isoDateStr);
    
    // Return v2 format (e.g., "20221001000000")
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    const seconds = String(date.getSeconds()).padStart(2, '0');
    
    return `${year}${month}${day}${hours}${minutes}${seconds}`;
  }

  // Usage
  const v1Date = '2022-10-01T00:00';
  const v2Date = convertToV2Date(v1Date);
  console.log(v2Date);  // '20221001000000'
  ```
</CodeGroup>

### Step 3: Update Response Handling

**v1: Returns array directly**

```json theme={null}
[
  {
    "image_id": "...",
    "image_url": "...",
    ...
  },
  ...
]
```

**v2: Returns structured object**

```json theme={null}
{
  "results": [
    {
      "image_id": "...",
      "image_url": "...",
      ...
    },
    ...
  ],
  "pagination": {
    "page": 1,
    "limit": 100,
    "total_count": 1500,
    "has_next": true,
    ...
  },
  "query": {...}
}
```

<CodeGroup>
  ```python Python - Before (v1) theme={null}
  response = requests.get(url, params=params)
  images = response.json()  # Direct array

  # Iterate images
  for img in images:
      print(img['image_url'])
  ```

  ```python Python - After (v2) theme={null}
  response = requests.get(url, params=params)
  data = response.json()
  images = data['results']  # Extract results array

  # Iterate images
  for img in images:
      print(img['image_url'])

  # Access pagination info
  if data['pagination']['has_next']:
      next_page = data['pagination']['next_page']
      print(f"Load page {next_page} for more results")
  ```
</CodeGroup>

<CodeGroup>
  ```javascript JavaScript - Before (v1) theme={null}
  const response = await fetch(url);
  const images = await response.json();  // Direct array

  // Iterate images
  images.forEach(img => {
    console.log(img.image_url);
  });
  ```

  ```javascript JavaScript - After (v2) theme={null}
  const response = await fetch(url);
  const data = await response.json();
  const images = data.results;  // Extract results array

  // Iterate images
  images.forEach(img => {
    console.log(img.image_url);
  });

  // Access pagination info
  if (data.pagination.has_next) {
    const nextPage = data.pagination.next_page;
    console.log(`Load page ${nextPage} for more results`);
  }
  ```
</CodeGroup>

### Step 4: Update Download Logic

**v1: Fixed 100-image limit**

```python theme={null}
# v1: Downloads max 100 images
response = requests.get(url, params={'download': True})
```

**v2: Estimate first, then download**

```python theme={null}
# v2: Get estimate first
estimate_response = requests.get(url, params={**params, 'download': 'estimate'})
estimate = estimate_response.json()

print(f"Will download {estimate['total_images']} images ({estimate['estimated_size_mb']} MB)")

# Then download if confirmed
if user_confirms:
    download_response = requests.get(url, params={**params, 'download': 'true'})
    with open('images.zip', 'wb') as f:
        f.write(download_response.content)
```

<CodeGroup>
  ```python Python - Complete Download Example (v2) theme={null}
  def download_images_v2(params):
      """Download images with v2 API (with estimate)"""
      base_url = 'https://avert.ldeo.columbia.edu/api/imagery/q'
      
      # Step 1: Get estimate
      estimate_params = {**params, 'download': 'estimate'}
      estimate_response = requests.get(base_url, params=estimate_params)
      estimate = estimate_response.json()
      
      total = estimate['total_images']
      size_mb = estimate['estimated_size_mb']
      time_sec = estimate['estimated_time_seconds']
      
      # Step 2: Show estimate and confirm
      print(f"Download estimate:")
      print(f"  - Images: {total}")
      print(f"  - Size: {size_mb} MB")
      print(f"  - Time: {time_sec} seconds")
      
      if estimate.get('warning'):
          print(f"  - Warning: {estimate['suggestion']}")
      
      confirm = input("Proceed with download? (y/n): ")
      
      if confirm.lower() == 'y':
          # Step 3: Download
          print(f"Downloading {total} images...")
          download_params = {**params, 'download': 'true'}
          download_response = requests.get(base_url, params=download_params)
          
          with open('avert_images.zip', 'wb') as f:
              f.write(download_response.content)
          
          print(f"✓ Downloaded to avert_images.zip")
      else:
          print("Download cancelled")

  # Usage
  params = {
      'imageType': 'infrared',
      'site': 'CLNE',
      'datefrom': '20251101000000',
      'dateto': '20251130235959',
      'freq': 'hourly'  # NEW: Use temporal sampling
  }
  download_images_v2(params)
  ```

  ```javascript JavaScript - Complete Download Example (v2) theme={null}
  async function downloadImagesV2(params) {
    const baseUrl = 'https://avert.ldeo.columbia.edu/api/imagery/q';
    
    // Step 1: Get estimate
    const estimateParams = new URLSearchParams({
      ...params,
      download: 'estimate'
    });
    
    const estimateResponse = await fetch(`${baseUrl}?${estimateParams}`);
    const estimate = await estimateResponse.json();
    
    const { total_images, estimated_size_mb, estimated_time_seconds, warning, suggestion } = estimate;
    
    // Step 2: Show estimate and confirm
    let message = `Download ${total_images} images (${estimated_size_mb} MB)?\n`;
    message += `Estimated time: ${estimated_time_seconds} seconds`;
    
    if (warning) {
      message += `\n\n⚠️ ${suggestion}`;
    }
    
    if (confirm(message)) {
      // Step 3: Trigger download
      const downloadParams = new URLSearchParams({
        ...params,
        download: 'true'
      });
      
      // Show loading indicator
      showLoadingSpinner(`Preparing ${total_images} images...`);
      
      // Trigger browser download
      window.location.href = `${baseUrl}?${downloadParams}`;
    }
  }

  // Usage
  const params = {
    imageType: 'infrared',
    site: 'CLNE',
    datefrom: '20251101000000',
    dateto: '20251130235959',
    freq: 'hourly'  // NEW: Use temporal sampling
  };

  downloadImagesV2(params);
  ```
</CodeGroup>

## New Features in v2

### 1. Condition Filters

v2 adds new filters for image quality and visibility conditions:

```python theme={null}
# Infrared filters
params = {
    'imageType': 'infrared',
    'site': 'CLNE',
    'is_degraded': False,      # NEW: Non-degraded only (infrared only)
    'low_visibility': False,   # NEW: Clear visibility (replaces has_fog/has_clouds)
    'is_empty': False,         # NEW: Non-empty files only
    'limit': 50
}

# Visible filters
params = {
    'imageType': 'visible',
    'site': 'CLNE',
    'is_night': False,         # NEW: Daytime only (visible only)
    'low_visibility': False,   # NEW: Clear visibility
    'is_empty': False,         # NEW: Non-empty files only
    'limit': 50
}
```

**Filter applicability by image type:**

| Filter           | Infrared | Visible | Description                             |
| ---------------- | -------- | ------- | --------------------------------------- |
| `is_empty`       | Yes      | Yes     | Empty/corrupted files                   |
| `is_night`       | Error    | Yes     | Nighttime (IR cameras work in darkness) |
| `is_degraded`    | Yes      | Error   | Dynamic range artifacts (IR only)       |
| `low_visibility` | Yes      | Yes     | Fog/clouds obstruction                  |

### 2. Temporal Sampling (freq)

Dramatically reduce dataset size while maintaining temporal coverage:

```python theme={null}
# Get one image per hour (60x data reduction)
params = {
    'site': 'VPMI',
    'freq': 'hourly',          # NEW: Temporal sampling
    'datefrom': '20251101000000',
    'dateto': '20251130235959'
}

# Get daily summaries (1440x data reduction)
params = {
    'site': 'VPMI',
    'freq': 'daily',           # NEW: One per day
    'datefrom': '20250101000000',
    'dateto': '20251231235959'
}
```

**freq options:**

* `all` - All captured images (default)
* `minutely` - One per minute
* `hourly` - One per hour
* `daily` - One per day

### 3. Enhanced Pagination

v2 provides comprehensive pagination metadata:

```python theme={null}
data = requests.get(url, params=params).json()

pagination = data['pagination']
print(f"Page {pagination['page']} of {pagination['total_pages']}")
print(f"Showing {pagination['count']} of {pagination['total_count']} results")

if pagination['has_next']:
    # Load next page
    next_page_params = {**params, 'page': pagination['next_page']}
```

### 4. Query Echo

v2 includes your query parameters in the response for debugging:

```python theme={null}
data = requests.get(url, params=params).json()

print("Your query:")
print(data['query'])
# Shows exactly what filters were applied
```

## Complete Migration Example

Here's a complete before/after example:

<CodeGroup>
  ```python v1 Code theme={null}
  import requests

  # v1 API
  url = "https://avert-legacy.ldeo.columbia.edu/api/imagery/infrared/query"
  params = {
      'site': 'CLNE',
      'vnum': 311240,
      'search_from': '2022-10-01T00:00',
      'search_to': '2022-10-31T23:59',
      'limit': 100
  }

  response = requests.get(url, params=params)
  images = response.json()  # Direct array

  print(f"Found {len(images)} images")
  for img in images[:5]:
      print(f"  - {img['image_id']}")

  # Download
  download_params = {**params, 'download': True}
  download_response = requests.get(url, params=download_params)
  with open('images.zip', 'wb') as f:
      f.write(download_response.content)
  ```

  ```python v2 Code theme={null}
  import requests

  # v2 API
  url = "https://avert.ldeo.columbia.edu/api/imagery/q"
  params = {
      'imageType': 'infrared',
      'site': 'CLNE',
      'vnum': 311240,
      'datefrom': '20221001000000',
      'dateto': '20221031235959',
      'is_degraded': False,      # NEW: Non-degraded only
      'low_visibility': False,   # NEW: Clear images only
      'freq': 'hourly',          # NEW: One per hour
      'limit': 100
  }

  response = requests.get(url, params=params)
  data = response.json()  # Structured response
  images = data['results']

  print(f"Found {len(images)} images (page {data['pagination']['page']} of {data['pagination']['total_pages']})")
  for img in images[:5]:
      print(f"  - {img['image_id']}")

  # Download with estimate
  estimate_response = requests.get(url, params={**params, 'download': 'estimate'})
  estimate = estimate_response.json()

  print(f"\nDownload estimate: {estimate['total_images']} images, {estimate['estimated_size_mb']} MB")

  if input("Download? (y/n): ").lower() == 'y':
      download_response = requests.get(url, params={**params, 'download': 'true'})
      with open('images.zip', 'wb') as f:
          f.write(download_response.content)
      print("✓ Downloaded")
  ```
</CodeGroup>

## Migration Checklist

Use this checklist to ensure complete migration:

<Steps>
  <Step title="Update endpoint URLs">
    * Replace `/api/imagery/infrared/query` with `/api/imagery/q?imageType=infrared`
    * Replace `/api/imagery/visible/query` with `/api/imagery/q?imageType=visible`
    * Update base URL to `https://avert.ldeo.columbia.edu`
  </Step>

  <Step title="Update parameters">
    * Replace `search_from` with `datefrom`
    * Replace `search_to` with `dateto`
    * Convert date format from ISO 8601 to `yyyymmddhhmmss`
    * Add `imageType` parameter
  </Step>

  <Step title="Update response handling">
    * Extract `data['results']` instead of using response directly
    * Access pagination via `data['pagination']`
    * Update loops to iterate over `data['results']`
  </Step>

  <Step title="Update download logic">
    * Add `download=estimate` call before downloading
    * Show estimate to users
    * Change `download=True` to `download='true'` (string)
  </Step>

  <Step title="Add new features (optional)">
    * Add condition filters (`is_empty`, `is_night`, `is_degraded`, `low_visibility`)
    * Add temporal sampling (`freq` parameter)
    * Implement pagination navigation
    * Add rate limit handling with retry logic
  </Step>

  <Step title="Test thoroughly">
    * Test basic queries
    * Test with filters
    * Test pagination
    * Test downloads with estimates
    * Test error handling
  </Step>
</Steps>

## Common Migration Issues

<AccordionGroup>
  <Accordion title="TypeError: 'list' object has no attribute 'results'" icon="bug">
    **Issue:** Trying to access `.results` on v1 response

    **Solution:** Update response handling

    ```python theme={null}
    # Wrong (v1 style with v2 API)
    images = response.json()
    for img in images:  # Error!
        ...

    # Correct (v2 style)
    data = response.json()
    images = data['results']
    for img in images:
        ...
    ```
  </Accordion>

  <Accordion title="400 Error: Invalid date format" icon="calendar">
    **Issue:** Using ISO 8601 date format with v2 API

    **Solution:** Convert to `yyyymmddhhmmss` format

    ```python theme={null}
    # Wrong
    'search_from': '2022-10-01T00:00'

    # Correct
    'datefrom': '20221001000000'
    ```
  </Accordion>

  <Accordion title="Images not filtered by type" icon="image">
    **Issue:** Forgot to add `imageType` parameter

    **Solution:** Explicitly specify image type

    ```python theme={null}
    # Wrong - might get wrong image type
    params = {'site': 'CLNE'}

    # Correct
    params = {'site': 'CLNE', 'imageType': 'infrared'}
    ```
  </Accordion>

  <Accordion title="Download returns JSON instead of ZIP" icon="file-zipper">
    **Issue:** Using wrong value for download parameter

    **Solution:** Use string 'true' not boolean True

    ```python theme={null}
    # Wrong
    params = {'download': True}  # Boolean

    # Correct
    params = {'download': 'true'}  # String
    ```
  </Accordion>
</AccordionGroup>

## Testing Your Migration

Test your migrated code with these queries:

### Test 1: Basic Query

```python theme={null}
# Should return structured response with results array
params = {
    'imageType': 'infrared',
    'site': 'CLNE',
    'limit': 5
}
data = requests.get('https://avert.ldeo.columbia.edu/api/imagery/q', params=params).json()

assert 'results' in data
assert 'pagination' in data
assert len(data['results']) <= 5
print("✓ Test 1 passed")
```

### Test 2: Date Filtering

```python theme={null}
# Should accept new date format
params = {
    'imageType': 'infrared',
    'site': 'CLNE',
    'datefrom': '20251101000000',
    'dateto': '20251101235959',
    'limit': 10
}
data = requests.get('https://avert.ldeo.columbia.edu/api/imagery/q', params=params).json()

assert data['query']['datefrom'] == '20251101000000'
print("✓ Test 2 passed")
```

### Test 3: Download Estimate

```python theme={null}
# Should return estimate without downloading
params = {
    'imageType': 'infrared',
    'site': 'CLNE',
    'limit': 10,
    'download': 'estimate'
}
estimate = requests.get('https://avert.ldeo.columbia.edu/api/imagery/q', params=params).json()

assert 'total_images' in estimate
assert 'estimated_size_mb' in estimate
print(f"✓ Test 3 passed - Would download {estimate['total_images']} images")
```

## Getting Help

<CardGroup cols={2}>
  <Card title="API v2 Documentation" icon="book" href="/api-reference/v2/introduction">
    Complete v2 API reference
  </Card>

  <Card title="Query Endpoint Reference" icon="code" href="/api-reference/v2/query-imagery">
    Detailed endpoint documentation
  </Card>

  <Card title="Contact Support" icon="envelope" href="mailto:avert-system@proton.me">
    Get help from the AVERT team
  </Card>

  <Card title="v1 Documentation" icon="clock-rotate-left" href="/api-reference/introduction">
    Legacy API reference (if needed)
  </Card>
</CardGroup>
