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

# Conteo por entidad

> Cuenta lugares por marca, institución o categoría dentro de un radio, una dirección o una entidad geográfica.

Cuenta los lugares activos de una zona, agrupados por **marca**, **institución** o **categoría**.

Los resultados vienen ordenados de mayor a menor, con un `geo_context` que ubica el área consultada. El área se define con `geo_filter` en uno de tres modos.

| Modo          | Campos                               | Ejemplo de uso                                      |
| ------------- | ------------------------------------ | --------------------------------------------------- |
| **Radio**     | `latitude`, `longitude`, `radius_km` | Competencia alrededor de una sucursal               |
| **Dirección** | `address`, `radius_km`               | Lo mismo, sin conocer las coordenadas               |
| **Entidad**   | `entity_type`, `entity_codes`        | Todos los comercios de un código postal o municipio |

<Tip>
  Empieza con `group_by: "category"` para ver el perfil comercial de la zona, y baja a `group_by: "brand"` cuando quieras nombres propios. Para obtener los lugares uno por uno, usa [listado por entidad](/es/atlas/lugares/listado-por-entidad).
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://client.tukanmx.com/tukan-atlas/places/grouped/" \
    -H "Authorization: Token $API_TUKAN" \
    -H "Content-Type: application/json" \
    -d '{
      "group_by": "brand",
      "geo_filter": {"latitude": 19.7060, "longitude": -101.1950, "radius_km": 2}
    }'
  ```

  ```python Python theme={null}
  import os
  import requests

  headers = {"Authorization": f"Token {os.environ['API_TUKAN']}"}

  # Marcas con mas presencia a 2 km del centro de Morelia
  resp = requests.post(
      "https://client.tukanmx.com/tukan-atlas/places/grouped/",
      headers=headers,
      json={
          "group_by": "brand",
          "geo_filter": {"latitude": 19.7060, "longitude": -101.1950, "radius_km": 2},
      },
  )
  resp.raise_for_status()
  for marca in resp.json()["results"][:3]:
      print(marca["brand_name"], marca["places_count"])
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "count": 45,
    "geo_context": {
      "state": "Michoacán de Ocampo",
      "municipality": "Morelia",
      "zipcode": "58000"
    },
    "results": [
      {
        "brand_id": 5303,
        "places_count": 36,
        "brand_name": "OXXO",
        "brand_name_en": "OXXO",
        "institution_id": "CCO8605231N4.MX",
        "institution_name": "Cadena Comercial OXXO",
        "legal_name": "Cadena Comercial OXXO, S.A. de C.V.",
        "categories": ["Minisuper"]
      },
      {
        "brand_id": 5801,
        "places_count": 21,
        "brand_name": "Farmacias Similares",
        "brand_name_en": "Farmacias Similares",
        "institution_id": "FSI970908ML5.MX",
        "institution_name": "Farmacias de Similares",
        "legal_name": "Farmacias de Similares S.A. de C.V.",
        "categories": ["Farmacia"]
      }
    ]
  }
  ```
</ResponseExample>


## OpenAPI

````yaml es/atlas/openapi-atlas.json POST /places/grouped/
openapi: 3.0.3
info:
  title: Atlas API
  description: >-
    API para consultar indicadores geoespaciales, catálogos geográficos y
    densidad de lugares en México.
  version: 1.0.0
  contact:
    name: Soporte Tukan
    email: contacto@tukanmx.com
servers:
  - url: https://client.tukanmx.com/tukan-atlas
    description: Servidor de producción
security:
  - tokenAuth: []
paths:
  /places/grouped/:
    post:
      summary: Densidad agrupada
      description: >-
        Cuenta los lugares activos dentro de un área geográfica, agrupados por
        marca, institución o categoría. El geo_filter soporta tres modos: radio
        (latitude, longitude, radius_km), dirección (address, radius_km) o
        polígono (entity_type, entity_codes). Los resultados se ordenan de mayor
        a menor por places_count.
      operationId: getPlacesGrouped
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlacesGroupedRequest'
      responses:
        '200':
          description: Conteo de lugares obtenido exitosamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlacesGroupedResponse'
        '400':
          description: Solicitud inválida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Token de autenticación inválido o ausente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    PlacesGroupedRequest:
      type: object
      required:
        - group_by
        - geo_filter
      properties:
        group_by:
          type: string
          enum:
            - brand
            - institution
            - category
          description: Criterio de agrupación de los resultados
        geo_filter:
          $ref: '#/components/schemas/GeoFilter'
    PlacesGroupedResponse:
      type: object
      properties:
        count:
          type: integer
        geo_context:
          $ref: '#/components/schemas/GeoContext'
        results:
          type: array
          description: La forma de cada elemento depende de group_by
          items:
            oneOf:
              - $ref: '#/components/schemas/PlacesGroupedBrandItem'
              - $ref: '#/components/schemas/PlacesGroupedInstitutionItem'
              - $ref: '#/components/schemas/PlacesGroupedCategoryItem'
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
    GeoFilter:
      type: object
      description: >-
        Filtro geográfico. Modos disponibles: radio (latitude, longitude,
        radius_km), dirección (address, radius_km) o polígono (entity_type,
        entity_codes).
      properties:
        latitude:
          type: number
          format: float
          description: Latitud del centro (modo radio)
          example: 19.706
        longitude:
          type: number
          format: float
          description: Longitud del centro (modo radio)
          example: -101.195
        radius_km:
          type: number
          format: float
          description: Radio en kilómetros (modo radio o dirección)
          example: 2
        address:
          type: string
          description: Dirección a geocodificar. Requiere radius_km. (modo dirección)
          example: Av. Madero Poniente 100, Centro, Morelia, Michoacán
        entity_type:
          type: string
          description: Tipo de entidad geográfica (modo polígono)
          enum:
            - STATE
            - MUNICIPALITY
            - ZIPCODE
            - LOCALITY
            - NEIGHBORHOOD
            - AGEB
            - URBAN_BLOCK
            - RURAL_BLOCK
            - COUNTRY
          example: ZIPCODE
        entity_codes:
          type: array
          items:
            type: string
          description: Uno o mas códigos de entidad geográfica (modo polígono)
          example:
            - '58000'
    GeoContext:
      type: object
      nullable: true
      properties:
        state:
          type: string
          nullable: true
          example: Michoacán de Ocampo
        municipality:
          type: string
          nullable: true
          example: Morelia
        zipcode:
          type: string
          nullable: true
          example: '58000'
    PlacesGroupedBrandItem:
      type: object
      description: Resultado cuando group_by = brand
      properties:
        brand_id:
          type: integer
          example: 5303
        brand_name:
          type: string
          example: OXXO
        brand_name_en:
          type: string
          example: OXXO
        institution_id:
          type: string
          example: CCO8605231N4.MX
        institution_name:
          type: string
          example: Cadena Comercial OXXO
        legal_name:
          type: string
          example: Cadena Comercial OXXO, S.A. de C.V.
        categories:
          type: array
          items:
            type: string
          example:
            - Minisuper
        places_count:
          type: integer
          example: 36
    PlacesGroupedInstitutionItem:
      type: object
      description: Resultado cuando group_by = institution
      properties:
        institution_id:
          type: string
          example: CCO8605231N4.MX
        institution_name:
          type: string
          example: Cadena Comercial OXXO
        legal_name:
          type: string
          example: Cadena Comercial OXXO, S.A. de C.V.
        places_count:
          type: integer
          example: 41
    PlacesGroupedCategoryItem:
      type: object
      description: Resultado cuando group_by = category
      properties:
        category_id:
          type: string
          example: bank
        name:
          type: string
          example: Banco
        name_en:
          type: string
          example: Bank
        category:
          type: string
          description: Grupo principal al que pertenece
          example: finance
        places_count:
          type: integer
          example: 17
  securitySchemes:
    tokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Token de autenticación. Formato requerido: Token tu-token (la palabra
        Token, un espacio y tu token). Solicita tu token escribiendo a
        contacto@tukanmx.com.

````