tenderapi-fr Profiles API

Alert profiles. Free: 1 profile, weekly email digest. Pro: up to 10, daily, email/Teams/webhook.

Operations 8

POST /profiles Create Profile #
GET /profiles List Profiles #
POST /profiles/preview/locked Ce que l'offre Pro ajouterait à ces critères #
POST /profiles/preview Tester un profil sans l'enregistrer #
GET /profiles/{profile_id} Get Profile #
PUT /profiles/{profile_id} Update Profile #
DELETE /profiles/{profile_id} Delete Profile #
POST /profiles/{profile_id}/set-active Set Profile Active #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/tenderapi-fr:tenderapi-fr-profiles-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

tenderapi-fr-profiles-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Tender Profiles API
  description: Unified French (BOAMP) + EU (TED) public procurement intelligence API. Search tenders, track award outcomes, discover winner patterns.
  version: 1.0.0
tags:
- name: Profiles
  description: 'Alert profiles. Free: 1 profile, weekly email digest. Pro: up to 10, daily, email/Teams/webhook.'
paths:
  /profiles:
    post:
      tags:
      - Profiles
      summary: Create Profile
      operationId: create_profile_profiles_post
      security:
      - APIKeyHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileCreate'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileOut'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    get:
      tags:
      - Profiles
      summary: List Profiles
      operationId: list_profiles_profiles_get
      security:
      - APIKeyHeader: []
      parameters:
      - name: include_inactive
        in: query
        required: false
        schema:
          type: boolean
          default: false
          title: Include Inactive
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProfileOut'
                title: Response List Profiles Profiles Get
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /profiles/preview/locked:
    post:
      tags:
      - Profiles
      summary: Ce que l'offre Pro ajouterait à ces critères
      description: 'Compteurs verrouillés pour un palier sans attributions ni échéances : nombre

        d''attributions sur 12 mois, de marchés arrivant à échéance, et un intitulé

        d''exemple. Jamais d''acheteur, de date ni de lignes. `locked=false` pour un

        palier qui a déjà ces données. Lecture seule, résultat mis en cache 1 h.'
      operationId: preview_locked_profiles_preview_locked_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfilePreviewIn'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LockedPreviewOut'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - APIKeyHeader: []
  /profiles/preview:
    post:
      tags:
      - Profiles
      summary: Tester un profil sans l'enregistrer
      description: 'Dry-run: compte les avis correspondant à ces critères, sans rien enregistrer.


        Renvoie le nombre d''avis actuellement ouverts (`count`) **et** le rythme réel

        d''arrivée (`last_24h`, `last_7d`, `last_30d`). Les deux ne disent pas la même

        chose : un profil étroit peut porter des centaines d''avis ouverts et ne rien

        délivrer pendant plusieurs jours, ce qui se lit à tort comme une panne. Le

        `last_30d` est le meilleur estimateur du volume d''alertes à attendre.


        Aucune destination (`webhook_url`/`email_to`) n''est requise ici. Lecture seule.'
      operationId: preview_profile_profiles_preview_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfilePreviewIn'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfilePreviewOut'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - APIKeyHeader: []
  /profiles/{profile_id}:
    get:
      tags:
      - Profiles
      summary: Get Profile
      operationId: get_profile_profiles__profile_id__get
      security:
      - APIKeyHeader: []
      parameters:
      - name: profile_id
        in: path
        required: true
        schema:
          type: integer
          title: Profile Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileOut'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    put:
      tags:
      - Profiles
      summary: Update Profile
      operationId: update_profile_profiles__profile_id__put
      security:
      - APIKeyHeader: []
      parameters:
      - name: profile_id
        in: path
        required: true
        schema:
          type: integer
          title: Profile Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileCreate'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileOut'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    delete:
      tags:
      - Profiles
      summary: Delete Profile
      operationId: delete_profile_profiles__profile_id__delete
      security:
      - APIKeyHeader: []
      parameters:
      - name: profile_id
        in: path
        required: true
        schema:
          type: integer
          title: Profile Id
      responses:
        '204':
          description: Successful Response
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /profiles/{profile_id}/set-active:
    post:
      tags:
      - Profiles
      summary: Set Profile Active
      description: Pause (active=false) or resume (active=true) a profile without deleting it.
      operationId: set_profile_active_profiles__profile_id__set_active_post
      security:
      - APIKeyHeader: []
      parameters:
      - name: profile_id
        in: path
        required: true
        schema:
          type: integer
          title: Profile Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/_ActiveUpdate'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileOut'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    LockedPreviewOut:
      properties:
        locked:
          type: boolean
          title: Locked
          description: 'False quand le palier a déjà attributions et échéances : rien à montrer.'
        awards_12m:
          type: integer
          title: Awards 12M
          description: 'Attributions des 12 derniers mois dont l''avis d''origine correspond aux critères (sous-estimé : ~77 % des attributions sont reliées à un avis).'
        renewals:
          type: integer
          title: Renewals
          description: Marchés (DECP, France) arrivant à échéance d'ici 3 à 18 mois.
        renewal_example:
          anyOf:
          - type: string
          - type: 'null'
          title: Renewal Example
          description: Intitulé d'un de ces marchés, sans acheteur ni date.
      type: object
      required:
      - locked
      - awards_12m
      - renewals
      title: LockedPreviewOut
      description: 'What the Pro tier would add on these criteria: counts and one title, never rows.'
    CriterionContribution:
      properties:
        criterion:
          type: string
          title: Criterion
          description: 'Champ concerné : keywords, cpv_codes, regions, departments, country, siret, budget ou descripteur_keywords.'
        label:
          type: string
          title: Label
          description: Nom lisible du critère, tel qu'affiché à l'utilisateur.
        values:
          items:
            type: string
          type: array
          title: Values
          description: Valeurs saisies pour ce critère.
        count_without:
          type: integer
          title: Count Without
          description: Avis ouverts si ce critère était retiré, les autres conservés.
        last_30d_without:
          type: integer
          title: Last 30D Without
          description: Parmi eux, ceux ingérés dans les 30 derniers jours.
        excluded_share:
          anyOf:
          - type: number
          - type: 'null'
          title: Excluded Share
          description: Part des avis des 30 derniers jours que ce critère retranche à ce que les autres retiennent déjà, entre 0 et 1. Null si le profil sans ce critère ne rend rien sur 30 jours, auquel cas le rapport n'a pas de sens.
      type: object
      required:
      - criterion
      - label
      - values
      - count_without
      - last_30d_without
      - excluded_share
      title: CriterionContribution
      description: 'What one criterion costs, measured by removing it and re-counting.


        Criteria are combined with AND, so every one of them narrows the result. A

        count on its own cannot show that: a profile can look perfectly healthy at

        34 open notices while a single criterion is holding back 143 more. The only

        way to expose it is the counterfactual, so we report the profile as it would

        be with this criterion dropped and everything else kept.


        Deliberately free of any verdict: no threshold, no flag, no advice. A

        department filter that drops 95% is doing exactly what its owner wanted, and

        we cannot tell that case apart from an accident. We report the numbers and

        the customer decides.'
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    ProfileCreate:
      properties:
        name:
          anyOf:
          - type: string
          - type: 'null'
          title: Name
        siret:
          anyOf:
          - type: string
            maxLength: 14
          - type: 'null'
          title: Siret
        keywords:
          items:
            type: string
          type: array
          maxItems: 20
          title: Keywords
        cpv_codes:
          items:
            type: string
          type: array
          maxItems: 20
          title: Cpv Codes
        regions:
          items:
            type: string
          type: array
          maxItems: 20
          title: Regions
        departments:
          items:
            type: string
          type: array
          maxItems: 20
          title: Departments
        country:
          items:
            type: string
          type: array
          maxItems: 20
          title: Country
          description: 'Codes pays ISO à 2 lettres, par exemple ["FR"]. Seul critère géographique qui porte sur les deux sources : les régions n''existent que sur le BOAMP.'
        budget_min:
          anyOf:
          - type: number
          - type: 'null'
          title: Budget Min
        budget_max:
          anyOf:
          - type: number
          - type: 'null'
          title: Budget Max
        channel:
          type: string
          title: Channel
          default: webhook
        webhook_url:
          anyOf:
          - type: string
          - type: 'null'
          title: Webhook Url
        email_to:
          anyOf:
          - type: string
          - type: 'null'
          title: Email To
        match_cpv_family:
          type: boolean
          title: Match Cpv Family
          default: false
        match_descripteur:
          type: boolean
          title: Match Descripteur
          default: false
        descripteur_keywords:
          items:
            type: string
          type: array
          maxItems: 20
          title: Descripteur Keywords
        active:
          type: boolean
          title: Active
          default: true
      additionalProperties: false
      type: object
      title: ProfileCreate
    ProfileOut:
      properties:
        id:
          type: integer
          title: Id
        name:
          anyOf:
          - type: string
          - type: 'null'
          title: Name
        siret:
          anyOf:
          - type: string
          - type: 'null'
          title: Siret
        keywords:
          items:
            type: string
          type: array
          title: Keywords
        cpv_codes:
          items:
            type: string
          type: array
          title: Cpv Codes
        regions:
          items:
            type: string
          type: array
          title: Regions
        departments:
          items:
            type: string
          type: array
          title: Departments
        country:
          items:
            type: string
          type: array
          title: Country
          default: []
        budget_min:
          anyOf:
          - type: number
          - type: 'null'
          title: Budget Min
        budget_max:
          anyOf:
          - type: number
          - type: 'null'
          title: Budget Max
        channel:
          type: string
          title: Channel
          default: webhook
        webhook_url:
          anyOf:
          - type: string
          - type: 'null'
          title: Webhook Url
        email_to:
          anyOf:
          - type: string
          - type: 'null'
          title: Email To
        active:
          type: boolean
          title: Active
        match_cpv_family:
          type: boolean
          title: Match Cpv Family
        match_descripteur:
          type: boolean
          title: Match Descripteur
          default: false
        descripteur_keywords:
          items:
            type: string
          type: array
          title: Descripteur Keywords
          default: []
        created_at:
          type: string
          format: date-time
          title: Created At
      type: object
      required:
      - id
      - name
      - siret
      - keywords
      - cpv_codes
      - regions
      - departments
      - budget_min
      - budget_max
      - webhook_url
      - active
      - match_cpv_family
      - created_at
      title: ProfileOut
    _ActiveUpdate:
      properties:
        active:
          type: boolean
          title: Active
      type: object
      required:
      - active
      title: _ActiveUpdate
    ProfilePreviewIn:
      properties:
        name:
          anyOf:
          - type: string
          - type: 'null'
          title: Name
        siret:
          anyOf:
          - type: string
            maxLength: 14
          - type: 'null'
          title: Siret
        keywords:
          items:
            type: string
          type: array
          maxItems: 20
          title: Keywords
        cpv_codes:
          items:
            type: string
          type: array
          maxItems: 20
          title: Cpv Codes
        regions:
          items:
            type: string
          type: array
          maxItems: 20
          title: Regions
        departments:
          items:
            type: string
          type: array
          maxItems: 20
          title: Departments
        country:
          items:
            type: string
          type: array
          maxItems: 20
          title: Country
          description: 'Codes pays ISO à 2 lettres, par exemple ["FR"]. Seul critère géographique qui porte sur les deux sources : les régions n''existent que sur le BOAMP.'
        budget_min:
          anyOf:
          - type: number
          - type: 'null'
          title: Budget Min
        budget_max:
          anyOf:
          - type: number
          - type: 'null'
          title: Budget Max
        channel:
          type: string
          title: Channel
          default: webhook
        webhook_url:
          anyOf:
          - type: string
          - type: 'null'
          title: Webhook Url
        email_to:
          anyOf:
          - type: string
          - type: 'null'
          title: Email To
        match_cpv_family:
          type: boolean
          title: Match Cpv Family
          default: false
        match_descripteur:
          type: boolean
          title: Match Descripteur
          default: false
        descripteur_keywords:
          items:
            type: string
          type: array
          maxItems: 20
          title: Descripteur Keywords
        active:
          type: boolean
          title: Active
          default: true
      additionalProperties: false
      type: object
      title: ProfilePreviewIn
      description: 'Dry-run input: the same criteria as ProfileCreate, minus the delivery rules.


        A preview delivers nothing, so requiring a webhook URL or an email address

        just to count matches is a dead end. It is what made a PRO customer''s first

        POST fail before he could see what his profile would return. Redefining the

        validator under the parent''s name replaces it (pydantic v2), so the delivery

        fields stay *accepted but unvalidated* and the dashboard can keep posting the

        full profile body unchanged.'
    ProfilePreviewOut:
      properties:
        count:
          type: integer
          title: Count
          description: Avis actuellement ouverts correspondant aux critères (publiés sur ~60 jours).
        last_24h:
          type: integer
          title: Last 24H
          description: Parmi eux, ceux ingérés dans les dernières 24 h, soit ce qu'un envoi quotidien (offre Pro) contiendrait. Le récapitulatif du lundi (offre gratuite) correspond à last_7d.
        last_7d:
          type: integer
          title: Last 7D
          description: Ingérés dans les 7 derniers jours.
        last_30d:
          type: integer
          title: Last 30D
          description: Ingérés dans les 30 derniers jours, le meilleur estimateur du rythme d'alertes.
        sample:
          items:
            type: string
          type: array
          title: Sample
          description: Titres des 5 premiers avis correspondants.
        per_criterion:
          items:
            $ref: '#/components/schemas/CriterionContribution'
          type: array
          title: Per Criterion
          description: Apport de chaque critère, mesuré en le retirant seul. Vide si le profil n'a qu'un seul critère, le retirer ne laissant alors aucun profil à mesurer.
      type: object
      required:
      - count
      - last_24h
      - last_7d
      - last_30d
      - sample
      title: ProfilePreviewOut
      description: What a profile would have delivered, not just what it currently sees.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
  securitySchemes:
    APIKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key