Ir para conteúdo
  • 0

ERRO API "429 TOO MANY REQUESTS"


FrancoAndre

Pergunta

  • Alunos

Olá, Senhores(as)!
 

Estou com duas bases que estão buscando dados da API do Runrunit e tudo deu certo na primeira chamada: consegui carregar os dados, fazer alguns gráficos, e as funções de paginação e parâmetros do Web.Contents funcionaram perfeitamente.
Estava tudo funcionando perfeitamente, o problema começou acontecer quando precisei trazer as informações da função fx_tasks_descricoes (destaque na imagem "invocando função personalizada") que são informações adicionais e necessárias para a primeira chamada. Abaixo o print do código.

image.png.f8b308f7943ebe648ad4f7deeb9bb5fd.png

image.png.6a10c853deb0f845dedd4db7b8a50218.png

 

O problema acredito que seja por conta da paginação da base f_Tarefas_fechadas, visto que só foi configurado lá e imagino que devo fazer também a paginação na função fx_tasks_descricoes, porém é essa a minha dúvida: como fazer uma paginação dentro de uma função. Abaixo o código da função:

image.png.aa5da07525583fad0ac1b5b05b480ea1.png

 

Aqui estão os dois códigos:

f_Tarefas_fechadas

let
    BlingCall = (pagina as number) =>
    let
        url = "https://runrun.it/api/v1.0/tasks",
        queryParameters = [
            page = Text.From(pagina),
            is_closed = "true",
            project_id = "3157584",
            is_assigned = "true"
        ],
        headers = [
            #"App-Key" = #"app-key",
            #"User-Token" = #"user-token",
            #"Content-Type" = "application/json"
        ],
        options = [
            Query = queryParameters,
            Headers = headers
        ],
    Fonte = Json.Document(Web.Contents(url, options))
    in
    Fonte,
    Source = Table.FromList(List.Generate( () => 1, each _ <= #"paginas-tasks", each _ +1), Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Invoked Custom Function" = Table.AddColumn(Source, "Modelo", each BlingCall([Column1])),
    #"Modelo Expandido" = Table.ExpandListColumn(#"Invoked Custom Function", "Modelo"),
    #"Modelo Expandido1" = Table.ExpandRecordColumn(#"Modelo Expandido", "Modelo", {"id", "title", "is_working_on", "user_id", "type_id", "project_id", "team_id", "board_id", "board_stage_id", "board_stage_position", "subtask_parent_position", "desired_date", "desired_date_with_time", "estimated_start_date", "estimated_delivery_date", "gantt_bar_start_date", "gantt_bar_end_date", "close_date", "was_reopened", "is_closed", "is_assigned", "on_going", "estimate_updated", "estimated_at", "queue_position", "created_at", "start_date", "desired_start_date", "current_estimate_seconds", "current_evaluator_id", "evaluation_status", "attachments_count", "tags_data", "client_name", "client_id", "project_name", "project_group_name", "project_group_id", "project_group_is_default", "project_sub_group_name", "project_sub_group_id", "project_sub_group_is_default", "type_name", "user_name", "board_name", "board_stage_name", "board_stage_description", "team_name", "type_color", "state", "overdue", "time_worked", "time_pending", "time_total", "time_progress", "activities_6_days_ago", "activities_5_days_ago", "activities_4_days_ago", "activities_3_days_ago", "activities_2_days_ago", "activities_1_days_ago", "activities_0_days_ago", "activities", "repetition_rule", "board_remaining_time", "stage_depart_estimated_at", "is_urgent", "points", "reestimate_count", "parent_ids", "opened_parent_ids", "parents_max_desired_date", "child_ids", "workflow_id", "checklist_id", "is_shared", "sharing_details", "subtask_ids", "subtasks_count", "subtasks_closed_count", "subtasks_count_progress", "is_subtask", "parent_task_id", "parent_task_title", "all_subtasks_time_worked", "all_subtasks_times_updating", "all_subtasks_time_total", "all_subtasks_time_progress", "current_level", "priority", "tag_list", "tags", "scheduled_start_time", "is_scheduled", "uid", "responsible_id", "task_state_id", "responsible_name", "task_state_name", "activities_7_days_ago", "repetition_rule_id", "current_worked_time", "estimated_delivery_date_updated", "last_estimated_at", "task_tags", "approved", "task_status_id", "task_status_name", "assignments", "follower_ids"}, {"id", "title", "is_working_on", "user_id", "type_id", "project_id", "team_id", "board_id", "board_stage_id", "board_stage_position", "subtask_parent_position", "desired_date", "desired_date_with_time", "estimated_start_date", "estimated_delivery_date", "gantt_bar_start_date", "gantt_bar_end_date", "close_date", "was_reopened", "is_closed", "is_assigned", "on_going", "estimate_updated", "estimated_at", "queue_position", "created_at", "start_date", "desired_start_date", "current_estimate_seconds", "current_evaluator_id", "evaluation_status", "attachments_count", "tags_data", "client_name", "client_id", "project_name", "project_group_name", "project_group_id", "project_group_is_default", "project_sub_group_name", "project_sub_group_id", "project_sub_group_is_default", "type_name", "user_name", "board_name", "board_stage_name", "board_stage_description", "team_name", "type_color", "state", "overdue", "time_worked", "time_pending", "time_total", "time_progress", "activities_6_days_ago", "activities_5_days_ago", "activities_4_days_ago", "activities_3_days_ago", "activities_2_days_ago", "activities_1_days_ago", "activities_0_days_ago", "activities", "repetition_rule", "board_remaining_time", "stage_depart_estimated_at", "is_urgent", "points", "reestimate_count", "parent_ids", "opened_parent_ids", "parents_max_desired_date", "child_ids", "workflow_id", "checklist_id", "is_shared", "sharing_details", "subtask_ids", "subtasks_count", "subtasks_closed_count", "subtasks_count_progress", "is_subtask", "parent_task_id", "parent_task_title", "all_subtasks_time_worked", "all_subtasks_times_updating", "all_subtasks_time_total", "all_subtasks_time_progress", "current_level", "priority", "tag_list", "tags", "scheduled_start_time", "is_scheduled", "uid", "responsible_id", "task_state_id", "responsible_name", "task_state_name", "activities_7_days_ago", "repetition_rule_id", "current_worked_time", "estimated_delivery_date_updated", "last_estimated_at", "task_tags", "approved", "task_status_id", "task_status_name", "assignments", "follower_ids"}),

    #"Outras Colunas Removidas" = Table.SelectColumns(#"Modelo Expandido1",{"id", "title", "project_id", "close_date", "is_closed", "start_date", "created_at", "board_stage_name", "time_total", "responsible_id"}),
    #"Colunas Reordenadas" = Table.ReorderColumns(#"Outras Colunas Removidas",{"id", "title", "project_id", "is_closed", "board_stage_name", "time_total",  "responsible_id", "created_at", "start_date", "close_date"}),
    #"Valor Substituído" = Table.ReplaceValue(#"Colunas Reordenadas","T"," ",Replacer.ReplaceText,{"created_at", "start_date", "close_date"}),
    #"Texto Extraído Antes do Delimitador" = Table.TransformColumns(#"Valor Substituído", {{"created_at", each Text.BeforeDelimiter(_, "-03:00"), type text}, {"start_date", each Text.BeforeDelimiter(_, "-03:00"), type text}, {"close_date", each Text.BeforeDelimiter(_, "-03:00"), type text}}),
    #"Tipo Alterado1" = Table.TransformColumnTypes(#"Texto Extraído Antes do Delimitador",{{"created_at", type datetime}, {"start_date", type datetime}, {"close_date", type datetime}}),
    #"Hora Inserida" = Table.AddColumn(#"Tipo Alterado1", "Hora created", each DateTime.Time([created_at]), type time),
    #"Hora Inserida1" = Table.AddColumn(#"Hora Inserida", "Hora Start", each DateTime.Time([start_date]), type time),
    #"Hora Inserida2" = Table.AddColumn(#"Hora Inserida1", "Hora Close", each DateTime.Time([close_date]), type time),
    #"Subtração da Hora Inserida" = Table.AddColumn(#"Hora Inserida2", "Tempo de Tarefa", each [close_date] - [start_date], type duration),
    #"Tipo Alterado" = Table.TransformColumnTypes(#"Subtração da Hora Inserida",{{"created_at", type date}, {"start_date", type date}, {"close_date", type date}, {"id", Int64.Type}, {"title", type text}, {"project_id", Int64.Type}, {"is_closed", type logical}, {"board_stage_name", type text}, {"time_total", Int64.Type}, {"responsible_id", type text}, {"Tempo de Tarefa", Int64.Type}}),
    
    #"Função Personalizada Invocada" = Table.AddColumn(#"Tipo Alterado", "Tasks_description", each fx_tasks_descricoes([id])),
    #"Tasks_description Expandido" = Table.ExpandTableColumn(#"Função Personalizada Invocada", "Tasks_description", {"Departamento", "Regional", "Supervisor", "Equipe", "Tipo de atendimento", "Deseja consultar por:", "Informe o número:", "Observações adicionais", "Justificativa", "Quantos serviços você tem disponível ?", "Número da UC"}, {"Departamento", "Regional", "Supervisor", "Equipe", "Tipo de atendimento", "Deseja consultar por:", "Informe o número:", "Observações adicionais", "Justificativa", "Quantos serviços você tem disponível ?", "Número da UC"}),
    #"Tipo Alterado2" = Table.Buffer(Table.TransformColumnTypes(#"Tasks_description Expandido",{{"Departamento", type text}, {"Regional", type text}, {"Supervisor", type text}, {"Equipe", type text}, {"Tipo de atendimento", type text}, {"Deseja consultar por:", type text}, {"Informe o número:", type text}, {"Observações adicionais", type text}, {"Justificativa", type text}, {"Quantos serviços você tem disponível ?", type text}, {"Número da UC", type text}}))
in
    #"Tipo Alterado2"

 

fx_tasks_descricoes

(TaskExterna as number) =>

let
    Fonte = Json.Document(Web.Contents("https://runrun.it/api/v1.0/tasks/"&Text.From(TaskExterna)&"/description", 
    [
        //Timeout=#duration(0, 1, 40, 0), 
        Headers=[
            #"App-Key"=#"app-key", 
            #"User-Token"=#"user-token", 
            #"Content-Type"="application/json"
        ]
    ])
    ),
    #"Convertido para Tabela1" = Record.ToTable(Fonte),
    #"Tipo Alterado" = Table.TransformColumnTypes(#"Convertido para Tabela1",{{"Value", type text}}),
    #"Valor Substituído" = Table.ReplaceValue(#"Tipo Alterado","<p>","",Replacer.ReplaceText,{"Value"}),
    #"Valor Substituído1" = Table.ReplaceValue(#"Valor Substituído","</p>","",Replacer.ReplaceText,{"Value"}),
    #"Tabela Transposta1" = Table.Transpose(#"Valor Substituído1"),
    #"Cabeçalhos Promovidos1" = Table.PromoteHeaders(#"Tabela Transposta1", [PromoteAllScalars=true]),
    #"Personalização Adicionada1" = Table.AddColumn(#"Cabeçalhos Promovidos1", "Personalizar", each Web.Page([description])),
    #"Personalizar Expandido" = Table.ExpandTableColumn(#"Personalização Adicionada1", "Personalizar", {"Data"}, {"Data"}),
    #"Data Expandido" = Table.ExpandTableColumn(#"Personalizar Expandido", "Data", {"Children"}, {"Children"}),
    #"Children Expandido" = Table.ExpandTableColumn(#"Data Expandido", "Children", {"Name", "Children"}, {"Name", "Children.1"}),
    #"Children.1 Expandido" = Table.ExpandTableColumn(#"Children Expandido", "Children.1", {"Children", "Text"}, {"Children", "Text"}),
    #"Children Expandido2" = Table.ExpandTableColumn(#"Children.1 Expandido", "Children", {"Children", "Text"}, {"Children.1", "Text.1"}),
    #"Children.1 Expandido1" = Table.ExpandTableColumn(#"Children Expandido2", "Children.1", {"Text"}, {"Text.2"}),
    #"Colunas Removidas" = Table.RemoveColumns(#"Children.1 Expandido1",{"Text.2", "Name", "description", "current_editor_id", "current_editor_name", "locked_at", "edited_at"}),
    #"Preenchido Abaixo1" = Table.FillDown(#"Colunas Removidas",{"Text.1"}),
    #"Valor Substituído2" = Table.ReplaceValue(#"Preenchido Abaixo1",null,"id",Replacer.ReplaceValue,{"Text.1"}),
    #"Personalização Adicionada2" = Table.AddColumn(#"Valor Substituído2", "Personalizar", each if [Text.1] = "id" then [id] else [Text]),
    #"Colunas Removidas1" = Table.RemoveColumns(#"Personalização Adicionada2",{"Text", "id"}),
    #"Linhas Filtradas" = Table.SelectRows(#"Colunas Removidas1", each ([Personalizar] <> null)),
    #"Tabela Transposta" = Table.Transpose(#"Linhas Filtradas"),
    #"Cabeçalhos Promovidos" = Table.PromoteHeaders(#"Tabela Transposta", [PromoteAllScalars=true])
in
    #"Cabeçalhos Promovidos"

 

 

Editado por FrancoAndre
Link para o comentário
Compartilhar em outros sites

8 respostass a esta questão

Posts Recomendados

  • 1
  • Alunos

@FrancoAndre infelizmente milagres desse tipo aí são difíceis de acontecer no Power Query. rsrsrs

Algumas APIs tem essas limitações e normalmente recomendo para os alunos trabalharem com Python ou Pentaho.

O erro 429 acontecerá em qualquer ferramenta se não controlar a quantidade de requisições por minuto por exemplo.

Você pode tentar o IsRetry do Web.Contents - PowerQuery M | Microsoft Learn

Mas pode ter problemas na limitação de requisições por minuto porque dessa maneira continuará a tentativa de requisição.

Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

@FrancoAndre bom dia!

Normal acontecer isso em algumas APIs.

Para tentar resolver no Power Query normalmente utilizo Function.InvokeAfter - PowerQuery M | Microsoft Learn

Mas nem sempre é possível faz isso no Power Query.

Além disso, verifiquei no código que você não trabalhou com RelativePath seria bom avaliar também porque é possível que na hora de atualizar os dados nos Serviços do Power BI (online).

Tem um vídeo no meu canal que mostra como trabalhar com RelativePath:

 

Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

Opa, Rafa! Agradeço a resposta.

Pesquisei um pouco sobre a função que citou e inclusive encontrei referências em outro post aqui no grupo e tentei aplicar no meu código afim de solucionar o erro das requests. 

Coloquei a função na etapa quando adiciono a coluna calculada e no parâmetro de #duration informei 1 segundo, ficando assim:

image.png.f0b42fcceab2f1b63db8c4e577150a21.png

 

O problema é que a base agora demora pra carrega e parece que a consulta está varrendo linha a linha:

image.png.4563792f9cd5bd5ca3062026fcfd8a2d.png

Aqui foi informado 1 página, mil registros.

Poderia sugerir uma melhor forma de contornar isso, desde já agradeço novamente a disposição.

Sobre o Relative.Path eu já tinha conhecimento, mas estou empenhado em primeiramente resolver essa etapa, mesmo assim agradeço a citação do seu canal que inclusive já assisti alguns vídeos de lá e me ajudaram bastante a fazer essas chamadas de API, conteúdo muito bom, parabéns! 😅

 

Editado por FrancoAndre
Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

Opa, Rafa!

Fiz uma tentativa utilizando IsRetry no código e, mesmo demorando um pouco, deu certo na primeira vez e até consegui carregar os dados. Quando fui dar um refresh aí que deu o erro novamente 😔.

Tem alguma outra sugestão? Se não, qual a sugestão para trabalhar com python?

Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

Essa parte eu trato no meu curso Chora API, principalmente na parte do Pentaho.

Python ainda estou estudando para entregar algo para os alunos.

Existem várias maneiras de resolver, pesquisando na internet você pode achar boas soluções.

Teria que ser algo com time.sleep e outra é trabalhar com while para ter sempre a resposta 200 nas requisições.

Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

Rafa, a função fx_tasks_descricoes depende de um id da tarefa para ser invocada, é possível criar da mesma forma no python? Se sim, e se possível, como seria a lógica para o código?

Entendi que teria que utilizar o script python e importar a base das tasks_descricoes, porém o script como informei antes vem com o endpoint da função dependendo de um id de tarefa. Já até entrei em contato com o suporte da Runrunit para verificar se além da documentação possui um endpoint que retorne uma lista das descrições de todas as ids.

Esse método com python é um pouco novo e confuso pra mim, se puder auxiliar ficarei grato pelo apoio.

Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

Bem antigo, mas olhando esse caso lembro que fiz algo que era um pouco mais complexo, onde eu teria que validar a data, depois o ultimo id e a partir desse  id pegar próximos 100 validado tempo de consulta e consegui, acho que é possível, mas eu criei 2 funções para fazer ele carregar, validar, carregar....

Link para o comentário
Compartilhar em outros sites

  • 0
  • Alunos

Já que o problema é a quantidade de requisições por segundo, tente encontrar o menor duration possível, que não ocorra o problema.

Pode ser menor que 1 segundo: = Function.InvokeAfter(() => fx(), #duration(0, 0, 0, 0.1))

Link para o comentário
Compartilhar em outros sites

Faça login para comentar

Você vai ser capaz de deixar um comentário após fazer o login



Entrar Agora
×
×
  • Criar Novo...